prinfer 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +84 -0
- package/dist/cli.cjs +219 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +196 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +181 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +70 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +141 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# prinfer
|
|
2
|
+
|
|
3
|
+
TypeScript type inference inspection tool. Inspect the inferred types of functions and variables in your TypeScript code.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install prinfer
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## CLI Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Basic usage
|
|
15
|
+
prinfer src/utils.ts myFunction
|
|
16
|
+
|
|
17
|
+
# With custom tsconfig
|
|
18
|
+
prinfer src/utils.ts myFunction --project ./tsconfig.json
|
|
19
|
+
|
|
20
|
+
# Show help
|
|
21
|
+
prinfer --help
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Output
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
(x: number, y: string) => boolean
|
|
28
|
+
returns: boolean
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Programmatic API
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { inferType } from "prinfer";
|
|
35
|
+
|
|
36
|
+
// Basic usage
|
|
37
|
+
const result = inferType("./src/utils.ts", "myFunction");
|
|
38
|
+
console.log(result.signature);
|
|
39
|
+
// => "(x: number, y: string) => boolean"
|
|
40
|
+
console.log(result.returnType);
|
|
41
|
+
// => "boolean"
|
|
42
|
+
|
|
43
|
+
// With custom tsconfig
|
|
44
|
+
const result2 = inferType("./src/utils.ts", "myFunction", "./tsconfig.json");
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### API Reference
|
|
48
|
+
|
|
49
|
+
#### `inferType(file, name, project?)`
|
|
50
|
+
|
|
51
|
+
Infer the type of a function or variable in a TypeScript file.
|
|
52
|
+
|
|
53
|
+
**Parameters:**
|
|
54
|
+
- `file` - Path to the TypeScript file
|
|
55
|
+
- `name` - Name of the function/variable to inspect
|
|
56
|
+
- `project` - Optional path to tsconfig.json
|
|
57
|
+
|
|
58
|
+
**Returns:** `InferredTypeResult`
|
|
59
|
+
- `signature` - The inferred type signature
|
|
60
|
+
- `returnType` - The return type (for functions)
|
|
61
|
+
|
|
62
|
+
#### Types
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
interface Options {
|
|
66
|
+
file: string;
|
|
67
|
+
name: string;
|
|
68
|
+
project?: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface InferredTypeResult {
|
|
72
|
+
signature: string;
|
|
73
|
+
returnType?: string;
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Requirements
|
|
78
|
+
|
|
79
|
+
- Node.js >= 18.0.0
|
|
80
|
+
- TypeScript >= 4.7.0 (peer dependency)
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
MIT
|
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/index.ts
|
|
27
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
28
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
29
|
+
|
|
30
|
+
// src/core.ts
|
|
31
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
32
|
+
var import_typescript = __toESM(require("typescript"), 1);
|
|
33
|
+
function findNearestTsconfig(startDir) {
|
|
34
|
+
return import_typescript.default.findConfigFile(startDir, import_typescript.default.sys.fileExists, "tsconfig.json") ?? void 0;
|
|
35
|
+
}
|
|
36
|
+
function loadProgram(entryFileAbs, project) {
|
|
37
|
+
const fileDir = import_node_path.default.dirname(entryFileAbs);
|
|
38
|
+
const tsconfigPath = project ? import_node_path.default.resolve(process.cwd(), project) : findNearestTsconfig(fileDir);
|
|
39
|
+
if (!tsconfigPath) {
|
|
40
|
+
return import_typescript.default.createProgram([entryFileAbs], {
|
|
41
|
+
target: import_typescript.default.ScriptTarget.ES2022,
|
|
42
|
+
module: import_typescript.default.ModuleKind.ESNext,
|
|
43
|
+
strict: true,
|
|
44
|
+
allowJs: true,
|
|
45
|
+
checkJs: false,
|
|
46
|
+
moduleResolution: import_typescript.default.ModuleResolutionKind.Bundler,
|
|
47
|
+
skipLibCheck: true
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const cfg = import_typescript.default.readConfigFile(tsconfigPath, import_typescript.default.sys.readFile);
|
|
51
|
+
if (cfg.error) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
import_typescript.default.flattenDiagnosticMessageText(cfg.error.messageText, "\n")
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
const parsed = import_typescript.default.parseJsonConfigFileContent(
|
|
57
|
+
cfg.config,
|
|
58
|
+
import_typescript.default.sys,
|
|
59
|
+
import_node_path.default.dirname(tsconfigPath)
|
|
60
|
+
);
|
|
61
|
+
return import_typescript.default.createProgram({
|
|
62
|
+
rootNames: parsed.fileNames,
|
|
63
|
+
options: parsed.options
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function isArrowOrFnExpr(n) {
|
|
67
|
+
return !!n && (import_typescript.default.isArrowFunction(n) || import_typescript.default.isFunctionExpression(n));
|
|
68
|
+
}
|
|
69
|
+
function nodeNameText(n) {
|
|
70
|
+
if (!n) return void 0;
|
|
71
|
+
if (import_typescript.default.isIdentifier(n)) return n.text;
|
|
72
|
+
if (import_typescript.default.isStringLiteral(n)) return n.text;
|
|
73
|
+
if (import_typescript.default.isNumericLiteral(n)) return n.text;
|
|
74
|
+
return void 0;
|
|
75
|
+
}
|
|
76
|
+
function isFunctionLikeNamed(node, name) {
|
|
77
|
+
if (import_typescript.default.isFunctionDeclaration(node) && node.name?.text === name) return true;
|
|
78
|
+
if (import_typescript.default.isVariableDeclaration(node) && import_typescript.default.isIdentifier(node.name) && node.name.text === name) {
|
|
79
|
+
return isArrowOrFnExpr(node.initializer);
|
|
80
|
+
}
|
|
81
|
+
if ((import_typescript.default.isMethodDeclaration(node) || import_typescript.default.isMethodSignature(node)) && nodeNameText(node.name) === name) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
if (import_typescript.default.isPropertyAssignment(node) && nodeNameText(node.name) === name) {
|
|
85
|
+
return isArrowOrFnExpr(node.initializer);
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
function findFirstMatch(sourceFile, name) {
|
|
90
|
+
let found;
|
|
91
|
+
const visit = (node) => {
|
|
92
|
+
if (found) return;
|
|
93
|
+
if (isFunctionLikeNamed(node, name)) {
|
|
94
|
+
found = node;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
import_typescript.default.forEachChild(node, visit);
|
|
98
|
+
};
|
|
99
|
+
visit(sourceFile);
|
|
100
|
+
return found;
|
|
101
|
+
}
|
|
102
|
+
function getTypeInfo(program, node) {
|
|
103
|
+
const checker = program.getTypeChecker();
|
|
104
|
+
let sig;
|
|
105
|
+
if (import_typescript.default.isFunctionDeclaration(node) || import_typescript.default.isMethodDeclaration(node)) {
|
|
106
|
+
sig = checker.getSignatureFromDeclaration(node) ?? void 0;
|
|
107
|
+
} else if (import_typescript.default.isVariableDeclaration(node)) {
|
|
108
|
+
const init = node.initializer;
|
|
109
|
+
if (isArrowOrFnExpr(init))
|
|
110
|
+
sig = checker.getSignatureFromDeclaration(init) ?? void 0;
|
|
111
|
+
} else if (import_typescript.default.isPropertyAssignment(node)) {
|
|
112
|
+
const init = node.initializer;
|
|
113
|
+
if (isArrowOrFnExpr(init))
|
|
114
|
+
sig = checker.getSignatureFromDeclaration(init) ?? void 0;
|
|
115
|
+
} else if (import_typescript.default.isMethodSignature(node)) {
|
|
116
|
+
sig = checker.getSignatureFromDeclaration(node) ?? void 0;
|
|
117
|
+
}
|
|
118
|
+
const flags = import_typescript.default.TypeFormatFlags.NoTruncation;
|
|
119
|
+
if (sig) {
|
|
120
|
+
const signature = checker.signatureToString(sig, void 0, flags);
|
|
121
|
+
const ret = checker.getReturnTypeOfSignature(sig);
|
|
122
|
+
const returnType = checker.typeToString(ret, void 0, flags);
|
|
123
|
+
return { signature, returnType };
|
|
124
|
+
}
|
|
125
|
+
const nodeWithName = node;
|
|
126
|
+
let nameNode = node;
|
|
127
|
+
if (nodeWithName.name && import_typescript.default.isIdentifier(nodeWithName.name)) {
|
|
128
|
+
nameNode = nodeWithName.name;
|
|
129
|
+
}
|
|
130
|
+
const t = checker.getTypeAtLocation(nameNode);
|
|
131
|
+
return { signature: checker.typeToString(t, void 0, flags) };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/index.ts
|
|
135
|
+
function inferType(file, name, project) {
|
|
136
|
+
const entryFileAbs = import_node_path2.default.resolve(process.cwd(), file);
|
|
137
|
+
if (!import_node_fs.default.existsSync(entryFileAbs)) {
|
|
138
|
+
throw new Error(`File not found: ${entryFileAbs}`);
|
|
139
|
+
}
|
|
140
|
+
const program = loadProgram(entryFileAbs, project);
|
|
141
|
+
const sourceFile = program.getSourceFile(entryFileAbs);
|
|
142
|
+
if (!sourceFile) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Could not load source file into the program (check tsconfig include/exclude): ${entryFileAbs}`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
const node = findFirstMatch(sourceFile, name);
|
|
148
|
+
if (!node) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`No function-like symbol named "${name}" found in ${entryFileAbs}`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
return getTypeInfo(program, node);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/cli.ts
|
|
157
|
+
var HELP = `
|
|
158
|
+
prinfer - TypeScript type inference inspection tool
|
|
159
|
+
|
|
160
|
+
Usage:
|
|
161
|
+
prinfer <file.ts> <name> [--project <tsconfig.json>]
|
|
162
|
+
|
|
163
|
+
Arguments:
|
|
164
|
+
file.ts Path to the TypeScript file
|
|
165
|
+
name Name of the function/variable to inspect
|
|
166
|
+
|
|
167
|
+
Options:
|
|
168
|
+
--project, -p Path to tsconfig.json (optional)
|
|
169
|
+
--help, -h Show this help message
|
|
170
|
+
|
|
171
|
+
Examples:
|
|
172
|
+
prinfer src/utils.ts myFunction
|
|
173
|
+
prinfer src/utils.ts myFunction --project ./tsconfig.json
|
|
174
|
+
`.trim();
|
|
175
|
+
function parseArgs(argv) {
|
|
176
|
+
const args = argv.slice(2);
|
|
177
|
+
if (args.includes("--help") || args.includes("-h") || args.length === 0) {
|
|
178
|
+
console.log(HELP);
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
const file = args[0];
|
|
182
|
+
const name = args[1];
|
|
183
|
+
if (!file || !name) {
|
|
184
|
+
console.error(
|
|
185
|
+
"Error: Both <file> and <name> arguments are required.\n"
|
|
186
|
+
);
|
|
187
|
+
console.log(HELP);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
let project;
|
|
191
|
+
const projectIdx = args.findIndex((a) => a === "--project" || a === "-p");
|
|
192
|
+
if (projectIdx >= 0) {
|
|
193
|
+
project = args[projectIdx + 1];
|
|
194
|
+
if (!project) {
|
|
195
|
+
console.error("Error: --project requires a path argument.\n");
|
|
196
|
+
console.log(HELP);
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return { file, name, project };
|
|
201
|
+
}
|
|
202
|
+
function main() {
|
|
203
|
+
const options = parseArgs(process.argv);
|
|
204
|
+
if (!options) {
|
|
205
|
+
process.exit(0);
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
const result = inferType(options.file, options.name, options.project);
|
|
209
|
+
console.log(result.signature);
|
|
210
|
+
if (result.returnType) {
|
|
211
|
+
console.log("returns:", result.returnType);
|
|
212
|
+
}
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.error(error.message);
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
main();
|
|
219
|
+
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core.ts","../src/cli.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { findFirstMatch, getTypeInfo, loadProgram } from \"./core.js\";\nimport type { InferredTypeResult, Options } from \"./types.js\";\n\n// Re-export types\nexport type { Options, InferredTypeResult };\n\n// Re-export core utilities\nexport {\n\tfindFirstMatch,\n\tfindNearestTsconfig,\n\tgetTypeInfo,\n\tloadProgram,\n} from \"./core.js\";\n\n/**\n * Infer the type of a function or variable in a TypeScript file\n *\n * @param file - Path to the TypeScript file\n * @param name - Name of the function/variable to inspect\n * @param project - Optional path to tsconfig.json\n * @returns The inferred type information\n * @throws Error if file not found or symbol not found\n *\n * @example\n * ```ts\n * import { inferType } from \"prinfer\";\n *\n * const result = inferType(\"./src/utils.ts\", \"myFunction\");\n * console.log(result.signature);\n * // => \"(x: number, y: string) => boolean\"\n * console.log(result.returnType);\n * // => \"boolean\"\n * ```\n */\nexport function inferType(\n\tfile: string,\n\tname: string,\n\tproject?: string,\n): InferredTypeResult {\n\tconst entryFileAbs = path.resolve(process.cwd(), file);\n\n\tif (!fs.existsSync(entryFileAbs)) {\n\t\tthrow new Error(`File not found: ${entryFileAbs}`);\n\t}\n\n\tconst program = loadProgram(entryFileAbs, project);\n\tconst sourceFile = program.getSourceFile(entryFileAbs);\n\n\tif (!sourceFile) {\n\t\tthrow new Error(\n\t\t\t`Could not load source file into the program (check tsconfig include/exclude): ${entryFileAbs}`,\n\t\t);\n\t}\n\n\tconst node = findFirstMatch(sourceFile, name);\n\tif (!node) {\n\t\tthrow new Error(\n\t\t\t`No function-like symbol named \"${name}\" found in ${entryFileAbs}`,\n\t\t);\n\t}\n\n\treturn getTypeInfo(program, node);\n}\n\n/**\n * Infer the type using an options object\n *\n * @param options - Options for type inference\n * @returns The inferred type information\n */\nexport function inferTypeFromOptions(options: Options): InferredTypeResult {\n\treturn inferType(options.file, options.name, options.project);\n}\n","import path from \"node:path\";\nimport ts from \"typescript\";\nimport type { InferredTypeResult } from \"./types.js\";\n\n/**\n * Find the nearest tsconfig.json from a starting directory\n */\nexport function findNearestTsconfig(startDir: string): string | undefined {\n\treturn (\n\t\tts.findConfigFile(startDir, ts.sys.fileExists, \"tsconfig.json\") ??\n\t\tundefined\n\t);\n}\n\n/**\n * Load a TypeScript program from an entry file\n */\nexport function loadProgram(\n\tentryFileAbs: string,\n\tproject?: string,\n): ts.Program {\n\tconst fileDir = path.dirname(entryFileAbs);\n\tconst tsconfigPath = project\n\t\t? path.resolve(process.cwd(), project)\n\t\t: findNearestTsconfig(fileDir);\n\n\tif (!tsconfigPath) {\n\t\t// Fallback: single-file program\n\t\treturn ts.createProgram([entryFileAbs], {\n\t\t\ttarget: ts.ScriptTarget.ES2022,\n\t\t\tmodule: ts.ModuleKind.ESNext,\n\t\t\tstrict: true,\n\t\t\tallowJs: true,\n\t\t\tcheckJs: false,\n\t\t\tmoduleResolution: ts.ModuleResolutionKind.Bundler,\n\t\t\tskipLibCheck: true,\n\t\t});\n\t}\n\n\tconst cfg = ts.readConfigFile(tsconfigPath, ts.sys.readFile);\n\tif (cfg.error) {\n\t\tthrow new Error(\n\t\t\tts.flattenDiagnosticMessageText(cfg.error.messageText, \"\\n\"),\n\t\t);\n\t}\n\n\tconst parsed = ts.parseJsonConfigFileContent(\n\t\tcfg.config,\n\t\tts.sys,\n\t\tpath.dirname(tsconfigPath),\n\t);\n\treturn ts.createProgram({\n\t\trootNames: parsed.fileNames,\n\t\toptions: parsed.options,\n\t});\n}\n\nfunction isArrowOrFnExpr(\n\tn: ts.Node | undefined,\n): n is ts.ArrowFunction | ts.FunctionExpression {\n\treturn !!n && (ts.isArrowFunction(n) || ts.isFunctionExpression(n));\n}\n\nfunction nodeNameText(\n\tn: ts.PropertyName | ts.BindingName | undefined,\n): string | undefined {\n\tif (!n) return undefined;\n\tif (ts.isIdentifier(n)) return n.text;\n\tif (ts.isStringLiteral(n)) return n.text;\n\tif (ts.isNumericLiteral(n)) return n.text;\n\treturn undefined; // ignore computed names etc.\n}\n\nfunction isFunctionLikeNamed(node: ts.Node, name: string): boolean {\n\t// function foo() {}\n\tif (ts.isFunctionDeclaration(node) && node.name?.text === name) return true;\n\n\t// const foo = () => {} / function() {}\n\tif (\n\t\tts.isVariableDeclaration(node) &&\n\t\tts.isIdentifier(node.name) &&\n\t\tnode.name.text === name\n\t) {\n\t\treturn isArrowOrFnExpr(node.initializer);\n\t}\n\n\t// class C { foo() {} } / interface signatures\n\tif (\n\t\t(ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) &&\n\t\tnodeNameText(node.name) === name\n\t) {\n\t\treturn true;\n\t}\n\n\t// object literal { foo() {} } via MethodDeclaration inside object literal is also MethodDeclaration\n\t// property assignment: { foo: () => {} }\n\tif (ts.isPropertyAssignment(node) && nodeNameText(node.name) === name) {\n\t\treturn isArrowOrFnExpr(node.initializer);\n\t}\n\n\treturn false;\n}\n\n/**\n * Find the first function-like node with the given name in a source file\n */\nexport function findFirstMatch(\n\tsourceFile: ts.SourceFile,\n\tname: string,\n): ts.Node | undefined {\n\tlet found: ts.Node | undefined;\n\n\tconst visit = (node: ts.Node) => {\n\t\tif (found) return;\n\t\tif (isFunctionLikeNamed(node, name)) {\n\t\t\tfound = node;\n\t\t\treturn;\n\t\t}\n\t\tts.forEachChild(node, visit);\n\t};\n\n\tvisit(sourceFile);\n\treturn found;\n}\n\n/**\n * Get type information for a node\n */\nexport function getTypeInfo(\n\tprogram: ts.Program,\n\tnode: ts.Node,\n): InferredTypeResult {\n\tconst checker = program.getTypeChecker();\n\n\tlet sig: ts.Signature | undefined;\n\n\t// Prefer getting the signature from a declaration/expression directly\n\tif (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {\n\t\tsig = checker.getSignatureFromDeclaration(node) ?? undefined;\n\t} else if (ts.isVariableDeclaration(node)) {\n\t\tconst init = node.initializer;\n\t\tif (isArrowOrFnExpr(init))\n\t\t\tsig = checker.getSignatureFromDeclaration(init) ?? undefined;\n\t} else if (ts.isPropertyAssignment(node)) {\n\t\tconst init = node.initializer;\n\t\tif (isArrowOrFnExpr(init))\n\t\t\tsig = checker.getSignatureFromDeclaration(init) ?? undefined;\n\t} else if (ts.isMethodSignature(node)) {\n\t\t// interfaces/types\n\t\tsig = checker.getSignatureFromDeclaration(node) ?? undefined;\n\t}\n\n\tconst flags = ts.TypeFormatFlags.NoTruncation;\n\n\tif (sig) {\n\t\tconst signature = checker.signatureToString(sig, undefined, flags);\n\t\tconst ret = checker.getReturnTypeOfSignature(sig);\n\t\tconst returnType = checker.typeToString(ret, undefined, flags);\n\t\treturn { signature, returnType };\n\t}\n\n\t// Fallback: type at the name location\n\tconst nodeWithName = node as unknown as { name?: ts.Node };\n\tlet nameNode: ts.Node = node;\n\tif (nodeWithName.name && ts.isIdentifier(nodeWithName.name)) {\n\t\tnameNode = nodeWithName.name;\n\t}\n\n\tconst t = checker.getTypeAtLocation(nameNode);\n\treturn { signature: checker.typeToString(t, undefined, flags) };\n}\n","#!/usr/bin/env bun\nimport { inferType } from \"./index.js\";\n\nconst HELP = `\nprinfer - TypeScript type inference inspection tool\n\nUsage:\n prinfer <file.ts> <name> [--project <tsconfig.json>]\n\nArguments:\n file.ts Path to the TypeScript file\n name Name of the function/variable to inspect\n\nOptions:\n --project, -p Path to tsconfig.json (optional)\n --help, -h Show this help message\n\nExamples:\n prinfer src/utils.ts myFunction\n prinfer src/utils.ts myFunction --project ./tsconfig.json\n`.trim();\n\ninterface CliOptions {\n\tfile: string;\n\tname: string;\n\tproject?: string;\n}\n\nfunction parseArgs(argv: string[]): CliOptions | null {\n\tconst args = argv.slice(2);\n\n\t// Check for help flag\n\tif (args.includes(\"--help\") || args.includes(\"-h\") || args.length === 0) {\n\t\tconsole.log(HELP);\n\t\treturn null;\n\t}\n\n\tconst file = args[0];\n\tconst name = args[1];\n\n\tif (!file || !name) {\n\t\tconsole.error(\n\t\t\t\"Error: Both <file> and <name> arguments are required.\\n\",\n\t\t);\n\t\tconsole.log(HELP);\n\t\tprocess.exit(1);\n\t}\n\n\t// Find project option\n\tlet project: string | undefined;\n\tconst projectIdx = args.findIndex((a) => a === \"--project\" || a === \"-p\");\n\tif (projectIdx >= 0) {\n\t\tproject = args[projectIdx + 1];\n\t\tif (!project) {\n\t\t\tconsole.error(\"Error: --project requires a path argument.\\n\");\n\t\t\tconsole.log(HELP);\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\treturn { file, name, project };\n}\n\nfunction main(): void {\n\tconst options = parseArgs(process.argv);\n\n\tif (!options) {\n\t\tprocess.exit(0);\n\t}\n\n\ttry {\n\t\tconst result = inferType(options.file, options.name, options.project);\n\n\t\tconsole.log(result.signature);\n\t\tif (result.returnType) {\n\t\t\tconsole.log(\"returns:\", result.returnType);\n\t\t}\n\t} catch (error) {\n\t\tconsole.error((error as Error).message);\n\t\tprocess.exit(1);\n\t}\n}\n\nmain();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qBAAe;AACf,IAAAA,oBAAiB;;;ACDjB,uBAAiB;AACjB,wBAAe;AAMR,SAAS,oBAAoB,UAAsC;AACzE,SACC,kBAAAC,QAAG,eAAe,UAAU,kBAAAA,QAAG,IAAI,YAAY,eAAe,KAC9D;AAEF;AAKO,SAAS,YACf,cACA,SACa;AACb,QAAM,UAAU,iBAAAC,QAAK,QAAQ,YAAY;AACzC,QAAM,eAAe,UAClB,iBAAAA,QAAK,QAAQ,QAAQ,IAAI,GAAG,OAAO,IACnC,oBAAoB,OAAO;AAE9B,MAAI,CAAC,cAAc;AAElB,WAAO,kBAAAD,QAAG,cAAc,CAAC,YAAY,GAAG;AAAA,MACvC,QAAQ,kBAAAA,QAAG,aAAa;AAAA,MACxB,QAAQ,kBAAAA,QAAG,WAAW;AAAA,MACtB,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,SAAS;AAAA,MACT,kBAAkB,kBAAAA,QAAG,qBAAqB;AAAA,MAC1C,cAAc;AAAA,IACf,CAAC;AAAA,EACF;AAEA,QAAM,MAAM,kBAAAA,QAAG,eAAe,cAAc,kBAAAA,QAAG,IAAI,QAAQ;AAC3D,MAAI,IAAI,OAAO;AACd,UAAM,IAAI;AAAA,MACT,kBAAAA,QAAG,6BAA6B,IAAI,MAAM,aAAa,IAAI;AAAA,IAC5D;AAAA,EACD;AAEA,QAAM,SAAS,kBAAAA,QAAG;AAAA,IACjB,IAAI;AAAA,IACJ,kBAAAA,QAAG;AAAA,IACH,iBAAAC,QAAK,QAAQ,YAAY;AAAA,EAC1B;AACA,SAAO,kBAAAD,QAAG,cAAc;AAAA,IACvB,WAAW,OAAO;AAAA,IAClB,SAAS,OAAO;AAAA,EACjB,CAAC;AACF;AAEA,SAAS,gBACR,GACgD;AAChD,SAAO,CAAC,CAAC,MAAM,kBAAAA,QAAG,gBAAgB,CAAC,KAAK,kBAAAA,QAAG,qBAAqB,CAAC;AAClE;AAEA,SAAS,aACR,GACqB;AACrB,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,kBAAAA,QAAG,aAAa,CAAC,EAAG,QAAO,EAAE;AACjC,MAAI,kBAAAA,QAAG,gBAAgB,CAAC,EAAG,QAAO,EAAE;AACpC,MAAI,kBAAAA,QAAG,iBAAiB,CAAC,EAAG,QAAO,EAAE;AACrC,SAAO;AACR;AAEA,SAAS,oBAAoB,MAAe,MAAuB;AAElE,MAAI,kBAAAA,QAAG,sBAAsB,IAAI,KAAK,KAAK,MAAM,SAAS,KAAM,QAAO;AAGvE,MACC,kBAAAA,QAAG,sBAAsB,IAAI,KAC7B,kBAAAA,QAAG,aAAa,KAAK,IAAI,KACzB,KAAK,KAAK,SAAS,MAClB;AACD,WAAO,gBAAgB,KAAK,WAAW;AAAA,EACxC;AAGA,OACE,kBAAAA,QAAG,oBAAoB,IAAI,KAAK,kBAAAA,QAAG,kBAAkB,IAAI,MAC1D,aAAa,KAAK,IAAI,MAAM,MAC3B;AACD,WAAO;AAAA,EACR;AAIA,MAAI,kBAAAA,QAAG,qBAAqB,IAAI,KAAK,aAAa,KAAK,IAAI,MAAM,MAAM;AACtE,WAAO,gBAAgB,KAAK,WAAW;AAAA,EACxC;AAEA,SAAO;AACR;AAKO,SAAS,eACf,YACA,MACsB;AACtB,MAAI;AAEJ,QAAM,QAAQ,CAAC,SAAkB;AAChC,QAAI,MAAO;AACX,QAAI,oBAAoB,MAAM,IAAI,GAAG;AACpC,cAAQ;AACR;AAAA,IACD;AACA,sBAAAA,QAAG,aAAa,MAAM,KAAK;AAAA,EAC5B;AAEA,QAAM,UAAU;AAChB,SAAO;AACR;AAKO,SAAS,YACf,SACA,MACqB;AACrB,QAAM,UAAU,QAAQ,eAAe;AAEvC,MAAI;AAGJ,MAAI,kBAAAA,QAAG,sBAAsB,IAAI,KAAK,kBAAAA,QAAG,oBAAoB,IAAI,GAAG;AACnE,UAAM,QAAQ,4BAA4B,IAAI,KAAK;AAAA,EACpD,WAAW,kBAAAA,QAAG,sBAAsB,IAAI,GAAG;AAC1C,UAAM,OAAO,KAAK;AAClB,QAAI,gBAAgB,IAAI;AACvB,YAAM,QAAQ,4BAA4B,IAAI,KAAK;AAAA,EACrD,WAAW,kBAAAA,QAAG,qBAAqB,IAAI,GAAG;AACzC,UAAM,OAAO,KAAK;AAClB,QAAI,gBAAgB,IAAI;AACvB,YAAM,QAAQ,4BAA4B,IAAI,KAAK;AAAA,EACrD,WAAW,kBAAAA,QAAG,kBAAkB,IAAI,GAAG;AAEtC,UAAM,QAAQ,4BAA4B,IAAI,KAAK;AAAA,EACpD;AAEA,QAAM,QAAQ,kBAAAA,QAAG,gBAAgB;AAEjC,MAAI,KAAK;AACR,UAAM,YAAY,QAAQ,kBAAkB,KAAK,QAAW,KAAK;AACjE,UAAM,MAAM,QAAQ,yBAAyB,GAAG;AAChD,UAAM,aAAa,QAAQ,aAAa,KAAK,QAAW,KAAK;AAC7D,WAAO,EAAE,WAAW,WAAW;AAAA,EAChC;AAGA,QAAM,eAAe;AACrB,MAAI,WAAoB;AACxB,MAAI,aAAa,QAAQ,kBAAAA,QAAG,aAAa,aAAa,IAAI,GAAG;AAC5D,eAAW,aAAa;AAAA,EACzB;AAEA,QAAM,IAAI,QAAQ,kBAAkB,QAAQ;AAC5C,SAAO,EAAE,WAAW,QAAQ,aAAa,GAAG,QAAW,KAAK,EAAE;AAC/D;;;ADtIO,SAAS,UACf,MACA,MACA,SACqB;AACrB,QAAM,eAAe,kBAAAE,QAAK,QAAQ,QAAQ,IAAI,GAAG,IAAI;AAErD,MAAI,CAAC,eAAAC,QAAG,WAAW,YAAY,GAAG;AACjC,UAAM,IAAI,MAAM,mBAAmB,YAAY,EAAE;AAAA,EAClD;AAEA,QAAM,UAAU,YAAY,cAAc,OAAO;AACjD,QAAM,aAAa,QAAQ,cAAc,YAAY;AAErD,MAAI,CAAC,YAAY;AAChB,UAAM,IAAI;AAAA,MACT,iFAAiF,YAAY;AAAA,IAC9F;AAAA,EACD;AAEA,QAAM,OAAO,eAAe,YAAY,IAAI;AAC5C,MAAI,CAAC,MAAM;AACV,UAAM,IAAI;AAAA,MACT,kCAAkC,IAAI,cAAc,YAAY;AAAA,IACjE;AAAA,EACD;AAEA,SAAO,YAAY,SAAS,IAAI;AACjC;;;AE7DA,IAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBX,KAAK;AAQP,SAAS,UAAU,MAAmC;AACrD,QAAM,OAAO,KAAK,MAAM,CAAC;AAGzB,MAAI,KAAK,SAAS,QAAQ,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,WAAW,GAAG;AACxE,YAAQ,IAAI,IAAI;AAChB,WAAO;AAAA,EACR;AAEA,QAAM,OAAO,KAAK,CAAC;AACnB,QAAM,OAAO,KAAK,CAAC;AAEnB,MAAI,CAAC,QAAQ,CAAC,MAAM;AACnB,YAAQ;AAAA,MACP;AAAA,IACD;AACA,YAAQ,IAAI,IAAI;AAChB,YAAQ,KAAK,CAAC;AAAA,EACf;AAGA,MAAI;AACJ,QAAM,aAAa,KAAK,UAAU,CAAC,MAAM,MAAM,eAAe,MAAM,IAAI;AACxE,MAAI,cAAc,GAAG;AACpB,cAAU,KAAK,aAAa,CAAC;AAC7B,QAAI,CAAC,SAAS;AACb,cAAQ,MAAM,8CAA8C;AAC5D,cAAQ,IAAI,IAAI;AAChB,cAAQ,KAAK,CAAC;AAAA,IACf;AAAA,EACD;AAEA,SAAO,EAAE,MAAM,MAAM,QAAQ;AAC9B;AAEA,SAAS,OAAa;AACrB,QAAM,UAAU,UAAU,QAAQ,IAAI;AAEtC,MAAI,CAAC,SAAS;AACb,YAAQ,KAAK,CAAC;AAAA,EACf;AAEA,MAAI;AACH,UAAM,SAAS,UAAU,QAAQ,MAAM,QAAQ,MAAM,QAAQ,OAAO;AAEpE,YAAQ,IAAI,OAAO,SAAS;AAC5B,QAAI,OAAO,YAAY;AACtB,cAAQ,IAAI,YAAY,OAAO,UAAU;AAAA,IAC1C;AAAA,EACD,SAAS,OAAO;AACf,YAAQ,MAAO,MAAgB,OAAO;AACtC,YAAQ,KAAK,CAAC;AAAA,EACf;AACD;AAEA,KAAK;","names":["import_node_path","ts","path","path","fs"]}
|
package/dist/cli.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path2 from "path";
|
|
6
|
+
|
|
7
|
+
// src/core.ts
|
|
8
|
+
import path from "path";
|
|
9
|
+
import ts from "typescript";
|
|
10
|
+
function findNearestTsconfig(startDir) {
|
|
11
|
+
return ts.findConfigFile(startDir, ts.sys.fileExists, "tsconfig.json") ?? void 0;
|
|
12
|
+
}
|
|
13
|
+
function loadProgram(entryFileAbs, project) {
|
|
14
|
+
const fileDir = path.dirname(entryFileAbs);
|
|
15
|
+
const tsconfigPath = project ? path.resolve(process.cwd(), project) : findNearestTsconfig(fileDir);
|
|
16
|
+
if (!tsconfigPath) {
|
|
17
|
+
return ts.createProgram([entryFileAbs], {
|
|
18
|
+
target: ts.ScriptTarget.ES2022,
|
|
19
|
+
module: ts.ModuleKind.ESNext,
|
|
20
|
+
strict: true,
|
|
21
|
+
allowJs: true,
|
|
22
|
+
checkJs: false,
|
|
23
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
24
|
+
skipLibCheck: true
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const cfg = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
|
28
|
+
if (cfg.error) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
ts.flattenDiagnosticMessageText(cfg.error.messageText, "\n")
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
const parsed = ts.parseJsonConfigFileContent(
|
|
34
|
+
cfg.config,
|
|
35
|
+
ts.sys,
|
|
36
|
+
path.dirname(tsconfigPath)
|
|
37
|
+
);
|
|
38
|
+
return ts.createProgram({
|
|
39
|
+
rootNames: parsed.fileNames,
|
|
40
|
+
options: parsed.options
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function isArrowOrFnExpr(n) {
|
|
44
|
+
return !!n && (ts.isArrowFunction(n) || ts.isFunctionExpression(n));
|
|
45
|
+
}
|
|
46
|
+
function nodeNameText(n) {
|
|
47
|
+
if (!n) return void 0;
|
|
48
|
+
if (ts.isIdentifier(n)) return n.text;
|
|
49
|
+
if (ts.isStringLiteral(n)) return n.text;
|
|
50
|
+
if (ts.isNumericLiteral(n)) return n.text;
|
|
51
|
+
return void 0;
|
|
52
|
+
}
|
|
53
|
+
function isFunctionLikeNamed(node, name) {
|
|
54
|
+
if (ts.isFunctionDeclaration(node) && node.name?.text === name) return true;
|
|
55
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === name) {
|
|
56
|
+
return isArrowOrFnExpr(node.initializer);
|
|
57
|
+
}
|
|
58
|
+
if ((ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) && nodeNameText(node.name) === name) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
if (ts.isPropertyAssignment(node) && nodeNameText(node.name) === name) {
|
|
62
|
+
return isArrowOrFnExpr(node.initializer);
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
function findFirstMatch(sourceFile, name) {
|
|
67
|
+
let found;
|
|
68
|
+
const visit = (node) => {
|
|
69
|
+
if (found) return;
|
|
70
|
+
if (isFunctionLikeNamed(node, name)) {
|
|
71
|
+
found = node;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
ts.forEachChild(node, visit);
|
|
75
|
+
};
|
|
76
|
+
visit(sourceFile);
|
|
77
|
+
return found;
|
|
78
|
+
}
|
|
79
|
+
function getTypeInfo(program, node) {
|
|
80
|
+
const checker = program.getTypeChecker();
|
|
81
|
+
let sig;
|
|
82
|
+
if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {
|
|
83
|
+
sig = checker.getSignatureFromDeclaration(node) ?? void 0;
|
|
84
|
+
} else if (ts.isVariableDeclaration(node)) {
|
|
85
|
+
const init = node.initializer;
|
|
86
|
+
if (isArrowOrFnExpr(init))
|
|
87
|
+
sig = checker.getSignatureFromDeclaration(init) ?? void 0;
|
|
88
|
+
} else if (ts.isPropertyAssignment(node)) {
|
|
89
|
+
const init = node.initializer;
|
|
90
|
+
if (isArrowOrFnExpr(init))
|
|
91
|
+
sig = checker.getSignatureFromDeclaration(init) ?? void 0;
|
|
92
|
+
} else if (ts.isMethodSignature(node)) {
|
|
93
|
+
sig = checker.getSignatureFromDeclaration(node) ?? void 0;
|
|
94
|
+
}
|
|
95
|
+
const flags = ts.TypeFormatFlags.NoTruncation;
|
|
96
|
+
if (sig) {
|
|
97
|
+
const signature = checker.signatureToString(sig, void 0, flags);
|
|
98
|
+
const ret = checker.getReturnTypeOfSignature(sig);
|
|
99
|
+
const returnType = checker.typeToString(ret, void 0, flags);
|
|
100
|
+
return { signature, returnType };
|
|
101
|
+
}
|
|
102
|
+
const nodeWithName = node;
|
|
103
|
+
let nameNode = node;
|
|
104
|
+
if (nodeWithName.name && ts.isIdentifier(nodeWithName.name)) {
|
|
105
|
+
nameNode = nodeWithName.name;
|
|
106
|
+
}
|
|
107
|
+
const t = checker.getTypeAtLocation(nameNode);
|
|
108
|
+
return { signature: checker.typeToString(t, void 0, flags) };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/index.ts
|
|
112
|
+
function inferType(file, name, project) {
|
|
113
|
+
const entryFileAbs = path2.resolve(process.cwd(), file);
|
|
114
|
+
if (!fs.existsSync(entryFileAbs)) {
|
|
115
|
+
throw new Error(`File not found: ${entryFileAbs}`);
|
|
116
|
+
}
|
|
117
|
+
const program = loadProgram(entryFileAbs, project);
|
|
118
|
+
const sourceFile = program.getSourceFile(entryFileAbs);
|
|
119
|
+
if (!sourceFile) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
`Could not load source file into the program (check tsconfig include/exclude): ${entryFileAbs}`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
const node = findFirstMatch(sourceFile, name);
|
|
125
|
+
if (!node) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
`No function-like symbol named "${name}" found in ${entryFileAbs}`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return getTypeInfo(program, node);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/cli.ts
|
|
134
|
+
var HELP = `
|
|
135
|
+
prinfer - TypeScript type inference inspection tool
|
|
136
|
+
|
|
137
|
+
Usage:
|
|
138
|
+
prinfer <file.ts> <name> [--project <tsconfig.json>]
|
|
139
|
+
|
|
140
|
+
Arguments:
|
|
141
|
+
file.ts Path to the TypeScript file
|
|
142
|
+
name Name of the function/variable to inspect
|
|
143
|
+
|
|
144
|
+
Options:
|
|
145
|
+
--project, -p Path to tsconfig.json (optional)
|
|
146
|
+
--help, -h Show this help message
|
|
147
|
+
|
|
148
|
+
Examples:
|
|
149
|
+
prinfer src/utils.ts myFunction
|
|
150
|
+
prinfer src/utils.ts myFunction --project ./tsconfig.json
|
|
151
|
+
`.trim();
|
|
152
|
+
function parseArgs(argv) {
|
|
153
|
+
const args = argv.slice(2);
|
|
154
|
+
if (args.includes("--help") || args.includes("-h") || args.length === 0) {
|
|
155
|
+
console.log(HELP);
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
const file = args[0];
|
|
159
|
+
const name = args[1];
|
|
160
|
+
if (!file || !name) {
|
|
161
|
+
console.error(
|
|
162
|
+
"Error: Both <file> and <name> arguments are required.\n"
|
|
163
|
+
);
|
|
164
|
+
console.log(HELP);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
let project;
|
|
168
|
+
const projectIdx = args.findIndex((a) => a === "--project" || a === "-p");
|
|
169
|
+
if (projectIdx >= 0) {
|
|
170
|
+
project = args[projectIdx + 1];
|
|
171
|
+
if (!project) {
|
|
172
|
+
console.error("Error: --project requires a path argument.\n");
|
|
173
|
+
console.log(HELP);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return { file, name, project };
|
|
178
|
+
}
|
|
179
|
+
function main() {
|
|
180
|
+
const options = parseArgs(process.argv);
|
|
181
|
+
if (!options) {
|
|
182
|
+
process.exit(0);
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
const result = inferType(options.file, options.name, options.project);
|
|
186
|
+
console.log(result.signature);
|
|
187
|
+
if (result.returnType) {
|
|
188
|
+
console.log("returns:", result.returnType);
|
|
189
|
+
}
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error(error.message);
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
main();
|
|
196
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core.ts","../src/cli.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { findFirstMatch, getTypeInfo, loadProgram } from \"./core.js\";\nimport type { InferredTypeResult, Options } from \"./types.js\";\n\n// Re-export types\nexport type { Options, InferredTypeResult };\n\n// Re-export core utilities\nexport {\n\tfindFirstMatch,\n\tfindNearestTsconfig,\n\tgetTypeInfo,\n\tloadProgram,\n} from \"./core.js\";\n\n/**\n * Infer the type of a function or variable in a TypeScript file\n *\n * @param file - Path to the TypeScript file\n * @param name - Name of the function/variable to inspect\n * @param project - Optional path to tsconfig.json\n * @returns The inferred type information\n * @throws Error if file not found or symbol not found\n *\n * @example\n * ```ts\n * import { inferType } from \"prinfer\";\n *\n * const result = inferType(\"./src/utils.ts\", \"myFunction\");\n * console.log(result.signature);\n * // => \"(x: number, y: string) => boolean\"\n * console.log(result.returnType);\n * // => \"boolean\"\n * ```\n */\nexport function inferType(\n\tfile: string,\n\tname: string,\n\tproject?: string,\n): InferredTypeResult {\n\tconst entryFileAbs = path.resolve(process.cwd(), file);\n\n\tif (!fs.existsSync(entryFileAbs)) {\n\t\tthrow new Error(`File not found: ${entryFileAbs}`);\n\t}\n\n\tconst program = loadProgram(entryFileAbs, project);\n\tconst sourceFile = program.getSourceFile(entryFileAbs);\n\n\tif (!sourceFile) {\n\t\tthrow new Error(\n\t\t\t`Could not load source file into the program (check tsconfig include/exclude): ${entryFileAbs}`,\n\t\t);\n\t}\n\n\tconst node = findFirstMatch(sourceFile, name);\n\tif (!node) {\n\t\tthrow new Error(\n\t\t\t`No function-like symbol named \"${name}\" found in ${entryFileAbs}`,\n\t\t);\n\t}\n\n\treturn getTypeInfo(program, node);\n}\n\n/**\n * Infer the type using an options object\n *\n * @param options - Options for type inference\n * @returns The inferred type information\n */\nexport function inferTypeFromOptions(options: Options): InferredTypeResult {\n\treturn inferType(options.file, options.name, options.project);\n}\n","import path from \"node:path\";\nimport ts from \"typescript\";\nimport type { InferredTypeResult } from \"./types.js\";\n\n/**\n * Find the nearest tsconfig.json from a starting directory\n */\nexport function findNearestTsconfig(startDir: string): string | undefined {\n\treturn (\n\t\tts.findConfigFile(startDir, ts.sys.fileExists, \"tsconfig.json\") ??\n\t\tundefined\n\t);\n}\n\n/**\n * Load a TypeScript program from an entry file\n */\nexport function loadProgram(\n\tentryFileAbs: string,\n\tproject?: string,\n): ts.Program {\n\tconst fileDir = path.dirname(entryFileAbs);\n\tconst tsconfigPath = project\n\t\t? path.resolve(process.cwd(), project)\n\t\t: findNearestTsconfig(fileDir);\n\n\tif (!tsconfigPath) {\n\t\t// Fallback: single-file program\n\t\treturn ts.createProgram([entryFileAbs], {\n\t\t\ttarget: ts.ScriptTarget.ES2022,\n\t\t\tmodule: ts.ModuleKind.ESNext,\n\t\t\tstrict: true,\n\t\t\tallowJs: true,\n\t\t\tcheckJs: false,\n\t\t\tmoduleResolution: ts.ModuleResolutionKind.Bundler,\n\t\t\tskipLibCheck: true,\n\t\t});\n\t}\n\n\tconst cfg = ts.readConfigFile(tsconfigPath, ts.sys.readFile);\n\tif (cfg.error) {\n\t\tthrow new Error(\n\t\t\tts.flattenDiagnosticMessageText(cfg.error.messageText, \"\\n\"),\n\t\t);\n\t}\n\n\tconst parsed = ts.parseJsonConfigFileContent(\n\t\tcfg.config,\n\t\tts.sys,\n\t\tpath.dirname(tsconfigPath),\n\t);\n\treturn ts.createProgram({\n\t\trootNames: parsed.fileNames,\n\t\toptions: parsed.options,\n\t});\n}\n\nfunction isArrowOrFnExpr(\n\tn: ts.Node | undefined,\n): n is ts.ArrowFunction | ts.FunctionExpression {\n\treturn !!n && (ts.isArrowFunction(n) || ts.isFunctionExpression(n));\n}\n\nfunction nodeNameText(\n\tn: ts.PropertyName | ts.BindingName | undefined,\n): string | undefined {\n\tif (!n) return undefined;\n\tif (ts.isIdentifier(n)) return n.text;\n\tif (ts.isStringLiteral(n)) return n.text;\n\tif (ts.isNumericLiteral(n)) return n.text;\n\treturn undefined; // ignore computed names etc.\n}\n\nfunction isFunctionLikeNamed(node: ts.Node, name: string): boolean {\n\t// function foo() {}\n\tif (ts.isFunctionDeclaration(node) && node.name?.text === name) return true;\n\n\t// const foo = () => {} / function() {}\n\tif (\n\t\tts.isVariableDeclaration(node) &&\n\t\tts.isIdentifier(node.name) &&\n\t\tnode.name.text === name\n\t) {\n\t\treturn isArrowOrFnExpr(node.initializer);\n\t}\n\n\t// class C { foo() {} } / interface signatures\n\tif (\n\t\t(ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) &&\n\t\tnodeNameText(node.name) === name\n\t) {\n\t\treturn true;\n\t}\n\n\t// object literal { foo() {} } via MethodDeclaration inside object literal is also MethodDeclaration\n\t// property assignment: { foo: () => {} }\n\tif (ts.isPropertyAssignment(node) && nodeNameText(node.name) === name) {\n\t\treturn isArrowOrFnExpr(node.initializer);\n\t}\n\n\treturn false;\n}\n\n/**\n * Find the first function-like node with the given name in a source file\n */\nexport function findFirstMatch(\n\tsourceFile: ts.SourceFile,\n\tname: string,\n): ts.Node | undefined {\n\tlet found: ts.Node | undefined;\n\n\tconst visit = (node: ts.Node) => {\n\t\tif (found) return;\n\t\tif (isFunctionLikeNamed(node, name)) {\n\t\t\tfound = node;\n\t\t\treturn;\n\t\t}\n\t\tts.forEachChild(node, visit);\n\t};\n\n\tvisit(sourceFile);\n\treturn found;\n}\n\n/**\n * Get type information for a node\n */\nexport function getTypeInfo(\n\tprogram: ts.Program,\n\tnode: ts.Node,\n): InferredTypeResult {\n\tconst checker = program.getTypeChecker();\n\n\tlet sig: ts.Signature | undefined;\n\n\t// Prefer getting the signature from a declaration/expression directly\n\tif (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {\n\t\tsig = checker.getSignatureFromDeclaration(node) ?? undefined;\n\t} else if (ts.isVariableDeclaration(node)) {\n\t\tconst init = node.initializer;\n\t\tif (isArrowOrFnExpr(init))\n\t\t\tsig = checker.getSignatureFromDeclaration(init) ?? undefined;\n\t} else if (ts.isPropertyAssignment(node)) {\n\t\tconst init = node.initializer;\n\t\tif (isArrowOrFnExpr(init))\n\t\t\tsig = checker.getSignatureFromDeclaration(init) ?? undefined;\n\t} else if (ts.isMethodSignature(node)) {\n\t\t// interfaces/types\n\t\tsig = checker.getSignatureFromDeclaration(node) ?? undefined;\n\t}\n\n\tconst flags = ts.TypeFormatFlags.NoTruncation;\n\n\tif (sig) {\n\t\tconst signature = checker.signatureToString(sig, undefined, flags);\n\t\tconst ret = checker.getReturnTypeOfSignature(sig);\n\t\tconst returnType = checker.typeToString(ret, undefined, flags);\n\t\treturn { signature, returnType };\n\t}\n\n\t// Fallback: type at the name location\n\tconst nodeWithName = node as unknown as { name?: ts.Node };\n\tlet nameNode: ts.Node = node;\n\tif (nodeWithName.name && ts.isIdentifier(nodeWithName.name)) {\n\t\tnameNode = nodeWithName.name;\n\t}\n\n\tconst t = checker.getTypeAtLocation(nameNode);\n\treturn { signature: checker.typeToString(t, undefined, flags) };\n}\n","#!/usr/bin/env bun\nimport { inferType } from \"./index.js\";\n\nconst HELP = `\nprinfer - TypeScript type inference inspection tool\n\nUsage:\n prinfer <file.ts> <name> [--project <tsconfig.json>]\n\nArguments:\n file.ts Path to the TypeScript file\n name Name of the function/variable to inspect\n\nOptions:\n --project, -p Path to tsconfig.json (optional)\n --help, -h Show this help message\n\nExamples:\n prinfer src/utils.ts myFunction\n prinfer src/utils.ts myFunction --project ./tsconfig.json\n`.trim();\n\ninterface CliOptions {\n\tfile: string;\n\tname: string;\n\tproject?: string;\n}\n\nfunction parseArgs(argv: string[]): CliOptions | null {\n\tconst args = argv.slice(2);\n\n\t// Check for help flag\n\tif (args.includes(\"--help\") || args.includes(\"-h\") || args.length === 0) {\n\t\tconsole.log(HELP);\n\t\treturn null;\n\t}\n\n\tconst file = args[0];\n\tconst name = args[1];\n\n\tif (!file || !name) {\n\t\tconsole.error(\n\t\t\t\"Error: Both <file> and <name> arguments are required.\\n\",\n\t\t);\n\t\tconsole.log(HELP);\n\t\tprocess.exit(1);\n\t}\n\n\t// Find project option\n\tlet project: string | undefined;\n\tconst projectIdx = args.findIndex((a) => a === \"--project\" || a === \"-p\");\n\tif (projectIdx >= 0) {\n\t\tproject = args[projectIdx + 1];\n\t\tif (!project) {\n\t\t\tconsole.error(\"Error: --project requires a path argument.\\n\");\n\t\t\tconsole.log(HELP);\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\treturn { file, name, project };\n}\n\nfunction main(): void {\n\tconst options = parseArgs(process.argv);\n\n\tif (!options) {\n\t\tprocess.exit(0);\n\t}\n\n\ttry {\n\t\tconst result = inferType(options.file, options.name, options.project);\n\n\t\tconsole.log(result.signature);\n\t\tif (result.returnType) {\n\t\t\tconsole.log(\"returns:\", result.returnType);\n\t\t}\n\t} catch (error) {\n\t\tconsole.error((error as Error).message);\n\t\tprocess.exit(1);\n\t}\n}\n\nmain();\n"],"mappings":"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","names":["path","path"]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
findFirstMatch: () => findFirstMatch,
|
|
34
|
+
findNearestTsconfig: () => findNearestTsconfig,
|
|
35
|
+
getTypeInfo: () => getTypeInfo,
|
|
36
|
+
inferType: () => inferType,
|
|
37
|
+
inferTypeFromOptions: () => inferTypeFromOptions,
|
|
38
|
+
loadProgram: () => loadProgram
|
|
39
|
+
});
|
|
40
|
+
module.exports = __toCommonJS(src_exports);
|
|
41
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
42
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
43
|
+
|
|
44
|
+
// src/core.ts
|
|
45
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
46
|
+
var import_typescript = __toESM(require("typescript"), 1);
|
|
47
|
+
function findNearestTsconfig(startDir) {
|
|
48
|
+
return import_typescript.default.findConfigFile(startDir, import_typescript.default.sys.fileExists, "tsconfig.json") ?? void 0;
|
|
49
|
+
}
|
|
50
|
+
function loadProgram(entryFileAbs, project) {
|
|
51
|
+
const fileDir = import_node_path.default.dirname(entryFileAbs);
|
|
52
|
+
const tsconfigPath = project ? import_node_path.default.resolve(process.cwd(), project) : findNearestTsconfig(fileDir);
|
|
53
|
+
if (!tsconfigPath) {
|
|
54
|
+
return import_typescript.default.createProgram([entryFileAbs], {
|
|
55
|
+
target: import_typescript.default.ScriptTarget.ES2022,
|
|
56
|
+
module: import_typescript.default.ModuleKind.ESNext,
|
|
57
|
+
strict: true,
|
|
58
|
+
allowJs: true,
|
|
59
|
+
checkJs: false,
|
|
60
|
+
moduleResolution: import_typescript.default.ModuleResolutionKind.Bundler,
|
|
61
|
+
skipLibCheck: true
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const cfg = import_typescript.default.readConfigFile(tsconfigPath, import_typescript.default.sys.readFile);
|
|
65
|
+
if (cfg.error) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
import_typescript.default.flattenDiagnosticMessageText(cfg.error.messageText, "\n")
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
const parsed = import_typescript.default.parseJsonConfigFileContent(
|
|
71
|
+
cfg.config,
|
|
72
|
+
import_typescript.default.sys,
|
|
73
|
+
import_node_path.default.dirname(tsconfigPath)
|
|
74
|
+
);
|
|
75
|
+
return import_typescript.default.createProgram({
|
|
76
|
+
rootNames: parsed.fileNames,
|
|
77
|
+
options: parsed.options
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function isArrowOrFnExpr(n) {
|
|
81
|
+
return !!n && (import_typescript.default.isArrowFunction(n) || import_typescript.default.isFunctionExpression(n));
|
|
82
|
+
}
|
|
83
|
+
function nodeNameText(n) {
|
|
84
|
+
if (!n) return void 0;
|
|
85
|
+
if (import_typescript.default.isIdentifier(n)) return n.text;
|
|
86
|
+
if (import_typescript.default.isStringLiteral(n)) return n.text;
|
|
87
|
+
if (import_typescript.default.isNumericLiteral(n)) return n.text;
|
|
88
|
+
return void 0;
|
|
89
|
+
}
|
|
90
|
+
function isFunctionLikeNamed(node, name) {
|
|
91
|
+
if (import_typescript.default.isFunctionDeclaration(node) && node.name?.text === name) return true;
|
|
92
|
+
if (import_typescript.default.isVariableDeclaration(node) && import_typescript.default.isIdentifier(node.name) && node.name.text === name) {
|
|
93
|
+
return isArrowOrFnExpr(node.initializer);
|
|
94
|
+
}
|
|
95
|
+
if ((import_typescript.default.isMethodDeclaration(node) || import_typescript.default.isMethodSignature(node)) && nodeNameText(node.name) === name) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
if (import_typescript.default.isPropertyAssignment(node) && nodeNameText(node.name) === name) {
|
|
99
|
+
return isArrowOrFnExpr(node.initializer);
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
function findFirstMatch(sourceFile, name) {
|
|
104
|
+
let found;
|
|
105
|
+
const visit = (node) => {
|
|
106
|
+
if (found) return;
|
|
107
|
+
if (isFunctionLikeNamed(node, name)) {
|
|
108
|
+
found = node;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
import_typescript.default.forEachChild(node, visit);
|
|
112
|
+
};
|
|
113
|
+
visit(sourceFile);
|
|
114
|
+
return found;
|
|
115
|
+
}
|
|
116
|
+
function getTypeInfo(program, node) {
|
|
117
|
+
const checker = program.getTypeChecker();
|
|
118
|
+
let sig;
|
|
119
|
+
if (import_typescript.default.isFunctionDeclaration(node) || import_typescript.default.isMethodDeclaration(node)) {
|
|
120
|
+
sig = checker.getSignatureFromDeclaration(node) ?? void 0;
|
|
121
|
+
} else if (import_typescript.default.isVariableDeclaration(node)) {
|
|
122
|
+
const init = node.initializer;
|
|
123
|
+
if (isArrowOrFnExpr(init))
|
|
124
|
+
sig = checker.getSignatureFromDeclaration(init) ?? void 0;
|
|
125
|
+
} else if (import_typescript.default.isPropertyAssignment(node)) {
|
|
126
|
+
const init = node.initializer;
|
|
127
|
+
if (isArrowOrFnExpr(init))
|
|
128
|
+
sig = checker.getSignatureFromDeclaration(init) ?? void 0;
|
|
129
|
+
} else if (import_typescript.default.isMethodSignature(node)) {
|
|
130
|
+
sig = checker.getSignatureFromDeclaration(node) ?? void 0;
|
|
131
|
+
}
|
|
132
|
+
const flags = import_typescript.default.TypeFormatFlags.NoTruncation;
|
|
133
|
+
if (sig) {
|
|
134
|
+
const signature = checker.signatureToString(sig, void 0, flags);
|
|
135
|
+
const ret = checker.getReturnTypeOfSignature(sig);
|
|
136
|
+
const returnType = checker.typeToString(ret, void 0, flags);
|
|
137
|
+
return { signature, returnType };
|
|
138
|
+
}
|
|
139
|
+
const nodeWithName = node;
|
|
140
|
+
let nameNode = node;
|
|
141
|
+
if (nodeWithName.name && import_typescript.default.isIdentifier(nodeWithName.name)) {
|
|
142
|
+
nameNode = nodeWithName.name;
|
|
143
|
+
}
|
|
144
|
+
const t = checker.getTypeAtLocation(nameNode);
|
|
145
|
+
return { signature: checker.typeToString(t, void 0, flags) };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/index.ts
|
|
149
|
+
function inferType(file, name, project) {
|
|
150
|
+
const entryFileAbs = import_node_path2.default.resolve(process.cwd(), file);
|
|
151
|
+
if (!import_node_fs.default.existsSync(entryFileAbs)) {
|
|
152
|
+
throw new Error(`File not found: ${entryFileAbs}`);
|
|
153
|
+
}
|
|
154
|
+
const program = loadProgram(entryFileAbs, project);
|
|
155
|
+
const sourceFile = program.getSourceFile(entryFileAbs);
|
|
156
|
+
if (!sourceFile) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
`Could not load source file into the program (check tsconfig include/exclude): ${entryFileAbs}`
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
const node = findFirstMatch(sourceFile, name);
|
|
162
|
+
if (!node) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
`No function-like symbol named "${name}" found in ${entryFileAbs}`
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
return getTypeInfo(program, node);
|
|
168
|
+
}
|
|
169
|
+
function inferTypeFromOptions(options) {
|
|
170
|
+
return inferType(options.file, options.name, options.project);
|
|
171
|
+
}
|
|
172
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
173
|
+
0 && (module.exports = {
|
|
174
|
+
findFirstMatch,
|
|
175
|
+
findNearestTsconfig,
|
|
176
|
+
getTypeInfo,
|
|
177
|
+
inferType,
|
|
178
|
+
inferTypeFromOptions,
|
|
179
|
+
loadProgram
|
|
180
|
+
});
|
|
181
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { findFirstMatch, getTypeInfo, loadProgram } from \"./core.js\";\nimport type { InferredTypeResult, Options } from \"./types.js\";\n\n// Re-export types\nexport type { Options, InferredTypeResult };\n\n// Re-export core utilities\nexport {\n\tfindFirstMatch,\n\tfindNearestTsconfig,\n\tgetTypeInfo,\n\tloadProgram,\n} from \"./core.js\";\n\n/**\n * Infer the type of a function or variable in a TypeScript file\n *\n * @param file - Path to the TypeScript file\n * @param name - Name of the function/variable to inspect\n * @param project - Optional path to tsconfig.json\n * @returns The inferred type information\n * @throws Error if file not found or symbol not found\n *\n * @example\n * ```ts\n * import { inferType } from \"prinfer\";\n *\n * const result = inferType(\"./src/utils.ts\", \"myFunction\");\n * console.log(result.signature);\n * // => \"(x: number, y: string) => boolean\"\n * console.log(result.returnType);\n * // => \"boolean\"\n * ```\n */\nexport function inferType(\n\tfile: string,\n\tname: string,\n\tproject?: string,\n): InferredTypeResult {\n\tconst entryFileAbs = path.resolve(process.cwd(), file);\n\n\tif (!fs.existsSync(entryFileAbs)) {\n\t\tthrow new Error(`File not found: ${entryFileAbs}`);\n\t}\n\n\tconst program = loadProgram(entryFileAbs, project);\n\tconst sourceFile = program.getSourceFile(entryFileAbs);\n\n\tif (!sourceFile) {\n\t\tthrow new Error(\n\t\t\t`Could not load source file into the program (check tsconfig include/exclude): ${entryFileAbs}`,\n\t\t);\n\t}\n\n\tconst node = findFirstMatch(sourceFile, name);\n\tif (!node) {\n\t\tthrow new Error(\n\t\t\t`No function-like symbol named \"${name}\" found in ${entryFileAbs}`,\n\t\t);\n\t}\n\n\treturn getTypeInfo(program, node);\n}\n\n/**\n * Infer the type using an options object\n *\n * @param options - Options for type inference\n * @returns The inferred type information\n */\nexport function inferTypeFromOptions(options: Options): InferredTypeResult {\n\treturn inferType(options.file, options.name, options.project);\n}\n","import path from \"node:path\";\nimport ts from \"typescript\";\nimport type { InferredTypeResult } from \"./types.js\";\n\n/**\n * Find the nearest tsconfig.json from a starting directory\n */\nexport function findNearestTsconfig(startDir: string): string | undefined {\n\treturn (\n\t\tts.findConfigFile(startDir, ts.sys.fileExists, \"tsconfig.json\") ??\n\t\tundefined\n\t);\n}\n\n/**\n * Load a TypeScript program from an entry file\n */\nexport function loadProgram(\n\tentryFileAbs: string,\n\tproject?: string,\n): ts.Program {\n\tconst fileDir = path.dirname(entryFileAbs);\n\tconst tsconfigPath = project\n\t\t? path.resolve(process.cwd(), project)\n\t\t: findNearestTsconfig(fileDir);\n\n\tif (!tsconfigPath) {\n\t\t// Fallback: single-file program\n\t\treturn ts.createProgram([entryFileAbs], {\n\t\t\ttarget: ts.ScriptTarget.ES2022,\n\t\t\tmodule: ts.ModuleKind.ESNext,\n\t\t\tstrict: true,\n\t\t\tallowJs: true,\n\t\t\tcheckJs: false,\n\t\t\tmoduleResolution: ts.ModuleResolutionKind.Bundler,\n\t\t\tskipLibCheck: true,\n\t\t});\n\t}\n\n\tconst cfg = ts.readConfigFile(tsconfigPath, ts.sys.readFile);\n\tif (cfg.error) {\n\t\tthrow new Error(\n\t\t\tts.flattenDiagnosticMessageText(cfg.error.messageText, \"\\n\"),\n\t\t);\n\t}\n\n\tconst parsed = ts.parseJsonConfigFileContent(\n\t\tcfg.config,\n\t\tts.sys,\n\t\tpath.dirname(tsconfigPath),\n\t);\n\treturn ts.createProgram({\n\t\trootNames: parsed.fileNames,\n\t\toptions: parsed.options,\n\t});\n}\n\nfunction isArrowOrFnExpr(\n\tn: ts.Node | undefined,\n): n is ts.ArrowFunction | ts.FunctionExpression {\n\treturn !!n && (ts.isArrowFunction(n) || ts.isFunctionExpression(n));\n}\n\nfunction nodeNameText(\n\tn: ts.PropertyName | ts.BindingName | undefined,\n): string | undefined {\n\tif (!n) return undefined;\n\tif (ts.isIdentifier(n)) return n.text;\n\tif (ts.isStringLiteral(n)) return n.text;\n\tif (ts.isNumericLiteral(n)) return n.text;\n\treturn undefined; // ignore computed names etc.\n}\n\nfunction isFunctionLikeNamed(node: ts.Node, name: string): boolean {\n\t// function foo() {}\n\tif (ts.isFunctionDeclaration(node) && node.name?.text === name) return true;\n\n\t// const foo = () => {} / function() {}\n\tif (\n\t\tts.isVariableDeclaration(node) &&\n\t\tts.isIdentifier(node.name) &&\n\t\tnode.name.text === name\n\t) {\n\t\treturn isArrowOrFnExpr(node.initializer);\n\t}\n\n\t// class C { foo() {} } / interface signatures\n\tif (\n\t\t(ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) &&\n\t\tnodeNameText(node.name) === name\n\t) {\n\t\treturn true;\n\t}\n\n\t// object literal { foo() {} } via MethodDeclaration inside object literal is also MethodDeclaration\n\t// property assignment: { foo: () => {} }\n\tif (ts.isPropertyAssignment(node) && nodeNameText(node.name) === name) {\n\t\treturn isArrowOrFnExpr(node.initializer);\n\t}\n\n\treturn false;\n}\n\n/**\n * Find the first function-like node with the given name in a source file\n */\nexport function findFirstMatch(\n\tsourceFile: ts.SourceFile,\n\tname: string,\n): ts.Node | undefined {\n\tlet found: ts.Node | undefined;\n\n\tconst visit = (node: ts.Node) => {\n\t\tif (found) return;\n\t\tif (isFunctionLikeNamed(node, name)) {\n\t\t\tfound = node;\n\t\t\treturn;\n\t\t}\n\t\tts.forEachChild(node, visit);\n\t};\n\n\tvisit(sourceFile);\n\treturn found;\n}\n\n/**\n * Get type information for a node\n */\nexport function getTypeInfo(\n\tprogram: ts.Program,\n\tnode: ts.Node,\n): InferredTypeResult {\n\tconst checker = program.getTypeChecker();\n\n\tlet sig: ts.Signature | undefined;\n\n\t// Prefer getting the signature from a declaration/expression directly\n\tif (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {\n\t\tsig = checker.getSignatureFromDeclaration(node) ?? undefined;\n\t} else if (ts.isVariableDeclaration(node)) {\n\t\tconst init = node.initializer;\n\t\tif (isArrowOrFnExpr(init))\n\t\t\tsig = checker.getSignatureFromDeclaration(init) ?? undefined;\n\t} else if (ts.isPropertyAssignment(node)) {\n\t\tconst init = node.initializer;\n\t\tif (isArrowOrFnExpr(init))\n\t\t\tsig = checker.getSignatureFromDeclaration(init) ?? undefined;\n\t} else if (ts.isMethodSignature(node)) {\n\t\t// interfaces/types\n\t\tsig = checker.getSignatureFromDeclaration(node) ?? undefined;\n\t}\n\n\tconst flags = ts.TypeFormatFlags.NoTruncation;\n\n\tif (sig) {\n\t\tconst signature = checker.signatureToString(sig, undefined, flags);\n\t\tconst ret = checker.getReturnTypeOfSignature(sig);\n\t\tconst returnType = checker.typeToString(ret, undefined, flags);\n\t\treturn { signature, returnType };\n\t}\n\n\t// Fallback: type at the name location\n\tconst nodeWithName = node as unknown as { name?: ts.Node };\n\tlet nameNode: ts.Node = node;\n\tif (nodeWithName.name && ts.isIdentifier(nodeWithName.name)) {\n\t\tnameNode = nodeWithName.name;\n\t}\n\n\tconst t = checker.getTypeAtLocation(nameNode);\n\treturn { signature: checker.typeToString(t, undefined, flags) };\n}\n"],"mappings":"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","names":["import_node_path","ts","path","path","fs"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Options for type inference
|
|
5
|
+
*/
|
|
6
|
+
interface Options {
|
|
7
|
+
/** Path to the TypeScript file */
|
|
8
|
+
file: string;
|
|
9
|
+
/** Name of the function/variable to inspect */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Optional path to tsconfig.json */
|
|
12
|
+
project?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Result of type inference
|
|
16
|
+
*/
|
|
17
|
+
interface InferredTypeResult {
|
|
18
|
+
/** The inferred type signature */
|
|
19
|
+
signature: string;
|
|
20
|
+
/** The return type (for functions) */
|
|
21
|
+
returnType?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Find the nearest tsconfig.json from a starting directory
|
|
26
|
+
*/
|
|
27
|
+
declare function findNearestTsconfig(startDir: string): string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Load a TypeScript program from an entry file
|
|
30
|
+
*/
|
|
31
|
+
declare function loadProgram(entryFileAbs: string, project?: string): ts.Program;
|
|
32
|
+
/**
|
|
33
|
+
* Find the first function-like node with the given name in a source file
|
|
34
|
+
*/
|
|
35
|
+
declare function findFirstMatch(sourceFile: ts.SourceFile, name: string): ts.Node | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Get type information for a node
|
|
38
|
+
*/
|
|
39
|
+
declare function getTypeInfo(program: ts.Program, node: ts.Node): InferredTypeResult;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Infer the type of a function or variable in a TypeScript file
|
|
43
|
+
*
|
|
44
|
+
* @param file - Path to the TypeScript file
|
|
45
|
+
* @param name - Name of the function/variable to inspect
|
|
46
|
+
* @param project - Optional path to tsconfig.json
|
|
47
|
+
* @returns The inferred type information
|
|
48
|
+
* @throws Error if file not found or symbol not found
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* import { inferType } from "prinfer";
|
|
53
|
+
*
|
|
54
|
+
* const result = inferType("./src/utils.ts", "myFunction");
|
|
55
|
+
* console.log(result.signature);
|
|
56
|
+
* // => "(x: number, y: string) => boolean"
|
|
57
|
+
* console.log(result.returnType);
|
|
58
|
+
* // => "boolean"
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
declare function inferType(file: string, name: string, project?: string): InferredTypeResult;
|
|
62
|
+
/**
|
|
63
|
+
* Infer the type using an options object
|
|
64
|
+
*
|
|
65
|
+
* @param options - Options for type inference
|
|
66
|
+
* @returns The inferred type information
|
|
67
|
+
*/
|
|
68
|
+
declare function inferTypeFromOptions(options: Options): InferredTypeResult;
|
|
69
|
+
|
|
70
|
+
export { type InferredTypeResult, type Options, findFirstMatch, findNearestTsconfig, getTypeInfo, inferType, inferTypeFromOptions, loadProgram };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Options for type inference
|
|
5
|
+
*/
|
|
6
|
+
interface Options {
|
|
7
|
+
/** Path to the TypeScript file */
|
|
8
|
+
file: string;
|
|
9
|
+
/** Name of the function/variable to inspect */
|
|
10
|
+
name: string;
|
|
11
|
+
/** Optional path to tsconfig.json */
|
|
12
|
+
project?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Result of type inference
|
|
16
|
+
*/
|
|
17
|
+
interface InferredTypeResult {
|
|
18
|
+
/** The inferred type signature */
|
|
19
|
+
signature: string;
|
|
20
|
+
/** The return type (for functions) */
|
|
21
|
+
returnType?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Find the nearest tsconfig.json from a starting directory
|
|
26
|
+
*/
|
|
27
|
+
declare function findNearestTsconfig(startDir: string): string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Load a TypeScript program from an entry file
|
|
30
|
+
*/
|
|
31
|
+
declare function loadProgram(entryFileAbs: string, project?: string): ts.Program;
|
|
32
|
+
/**
|
|
33
|
+
* Find the first function-like node with the given name in a source file
|
|
34
|
+
*/
|
|
35
|
+
declare function findFirstMatch(sourceFile: ts.SourceFile, name: string): ts.Node | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Get type information for a node
|
|
38
|
+
*/
|
|
39
|
+
declare function getTypeInfo(program: ts.Program, node: ts.Node): InferredTypeResult;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Infer the type of a function or variable in a TypeScript file
|
|
43
|
+
*
|
|
44
|
+
* @param file - Path to the TypeScript file
|
|
45
|
+
* @param name - Name of the function/variable to inspect
|
|
46
|
+
* @param project - Optional path to tsconfig.json
|
|
47
|
+
* @returns The inferred type information
|
|
48
|
+
* @throws Error if file not found or symbol not found
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* import { inferType } from "prinfer";
|
|
53
|
+
*
|
|
54
|
+
* const result = inferType("./src/utils.ts", "myFunction");
|
|
55
|
+
* console.log(result.signature);
|
|
56
|
+
* // => "(x: number, y: string) => boolean"
|
|
57
|
+
* console.log(result.returnType);
|
|
58
|
+
* // => "boolean"
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
declare function inferType(file: string, name: string, project?: string): InferredTypeResult;
|
|
62
|
+
/**
|
|
63
|
+
* Infer the type using an options object
|
|
64
|
+
*
|
|
65
|
+
* @param options - Options for type inference
|
|
66
|
+
* @returns The inferred type information
|
|
67
|
+
*/
|
|
68
|
+
declare function inferTypeFromOptions(options: Options): InferredTypeResult;
|
|
69
|
+
|
|
70
|
+
export { type InferredTypeResult, type Options, findFirstMatch, findNearestTsconfig, getTypeInfo, inferType, inferTypeFromOptions, loadProgram };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path2 from "path";
|
|
4
|
+
|
|
5
|
+
// src/core.ts
|
|
6
|
+
import path from "path";
|
|
7
|
+
import ts from "typescript";
|
|
8
|
+
function findNearestTsconfig(startDir) {
|
|
9
|
+
return ts.findConfigFile(startDir, ts.sys.fileExists, "tsconfig.json") ?? void 0;
|
|
10
|
+
}
|
|
11
|
+
function loadProgram(entryFileAbs, project) {
|
|
12
|
+
const fileDir = path.dirname(entryFileAbs);
|
|
13
|
+
const tsconfigPath = project ? path.resolve(process.cwd(), project) : findNearestTsconfig(fileDir);
|
|
14
|
+
if (!tsconfigPath) {
|
|
15
|
+
return ts.createProgram([entryFileAbs], {
|
|
16
|
+
target: ts.ScriptTarget.ES2022,
|
|
17
|
+
module: ts.ModuleKind.ESNext,
|
|
18
|
+
strict: true,
|
|
19
|
+
allowJs: true,
|
|
20
|
+
checkJs: false,
|
|
21
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
22
|
+
skipLibCheck: true
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const cfg = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
|
26
|
+
if (cfg.error) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
ts.flattenDiagnosticMessageText(cfg.error.messageText, "\n")
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
const parsed = ts.parseJsonConfigFileContent(
|
|
32
|
+
cfg.config,
|
|
33
|
+
ts.sys,
|
|
34
|
+
path.dirname(tsconfigPath)
|
|
35
|
+
);
|
|
36
|
+
return ts.createProgram({
|
|
37
|
+
rootNames: parsed.fileNames,
|
|
38
|
+
options: parsed.options
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function isArrowOrFnExpr(n) {
|
|
42
|
+
return !!n && (ts.isArrowFunction(n) || ts.isFunctionExpression(n));
|
|
43
|
+
}
|
|
44
|
+
function nodeNameText(n) {
|
|
45
|
+
if (!n) return void 0;
|
|
46
|
+
if (ts.isIdentifier(n)) return n.text;
|
|
47
|
+
if (ts.isStringLiteral(n)) return n.text;
|
|
48
|
+
if (ts.isNumericLiteral(n)) return n.text;
|
|
49
|
+
return void 0;
|
|
50
|
+
}
|
|
51
|
+
function isFunctionLikeNamed(node, name) {
|
|
52
|
+
if (ts.isFunctionDeclaration(node) && node.name?.text === name) return true;
|
|
53
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === name) {
|
|
54
|
+
return isArrowOrFnExpr(node.initializer);
|
|
55
|
+
}
|
|
56
|
+
if ((ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) && nodeNameText(node.name) === name) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
if (ts.isPropertyAssignment(node) && nodeNameText(node.name) === name) {
|
|
60
|
+
return isArrowOrFnExpr(node.initializer);
|
|
61
|
+
}
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
function findFirstMatch(sourceFile, name) {
|
|
65
|
+
let found;
|
|
66
|
+
const visit = (node) => {
|
|
67
|
+
if (found) return;
|
|
68
|
+
if (isFunctionLikeNamed(node, name)) {
|
|
69
|
+
found = node;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
ts.forEachChild(node, visit);
|
|
73
|
+
};
|
|
74
|
+
visit(sourceFile);
|
|
75
|
+
return found;
|
|
76
|
+
}
|
|
77
|
+
function getTypeInfo(program, node) {
|
|
78
|
+
const checker = program.getTypeChecker();
|
|
79
|
+
let sig;
|
|
80
|
+
if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {
|
|
81
|
+
sig = checker.getSignatureFromDeclaration(node) ?? void 0;
|
|
82
|
+
} else if (ts.isVariableDeclaration(node)) {
|
|
83
|
+
const init = node.initializer;
|
|
84
|
+
if (isArrowOrFnExpr(init))
|
|
85
|
+
sig = checker.getSignatureFromDeclaration(init) ?? void 0;
|
|
86
|
+
} else if (ts.isPropertyAssignment(node)) {
|
|
87
|
+
const init = node.initializer;
|
|
88
|
+
if (isArrowOrFnExpr(init))
|
|
89
|
+
sig = checker.getSignatureFromDeclaration(init) ?? void 0;
|
|
90
|
+
} else if (ts.isMethodSignature(node)) {
|
|
91
|
+
sig = checker.getSignatureFromDeclaration(node) ?? void 0;
|
|
92
|
+
}
|
|
93
|
+
const flags = ts.TypeFormatFlags.NoTruncation;
|
|
94
|
+
if (sig) {
|
|
95
|
+
const signature = checker.signatureToString(sig, void 0, flags);
|
|
96
|
+
const ret = checker.getReturnTypeOfSignature(sig);
|
|
97
|
+
const returnType = checker.typeToString(ret, void 0, flags);
|
|
98
|
+
return { signature, returnType };
|
|
99
|
+
}
|
|
100
|
+
const nodeWithName = node;
|
|
101
|
+
let nameNode = node;
|
|
102
|
+
if (nodeWithName.name && ts.isIdentifier(nodeWithName.name)) {
|
|
103
|
+
nameNode = nodeWithName.name;
|
|
104
|
+
}
|
|
105
|
+
const t = checker.getTypeAtLocation(nameNode);
|
|
106
|
+
return { signature: checker.typeToString(t, void 0, flags) };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/index.ts
|
|
110
|
+
function inferType(file, name, project) {
|
|
111
|
+
const entryFileAbs = path2.resolve(process.cwd(), file);
|
|
112
|
+
if (!fs.existsSync(entryFileAbs)) {
|
|
113
|
+
throw new Error(`File not found: ${entryFileAbs}`);
|
|
114
|
+
}
|
|
115
|
+
const program = loadProgram(entryFileAbs, project);
|
|
116
|
+
const sourceFile = program.getSourceFile(entryFileAbs);
|
|
117
|
+
if (!sourceFile) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`Could not load source file into the program (check tsconfig include/exclude): ${entryFileAbs}`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
const node = findFirstMatch(sourceFile, name);
|
|
123
|
+
if (!node) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`No function-like symbol named "${name}" found in ${entryFileAbs}`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
return getTypeInfo(program, node);
|
|
129
|
+
}
|
|
130
|
+
function inferTypeFromOptions(options) {
|
|
131
|
+
return inferType(options.file, options.name, options.project);
|
|
132
|
+
}
|
|
133
|
+
export {
|
|
134
|
+
findFirstMatch,
|
|
135
|
+
findNearestTsconfig,
|
|
136
|
+
getTypeInfo,
|
|
137
|
+
inferType,
|
|
138
|
+
inferTypeFromOptions,
|
|
139
|
+
loadProgram
|
|
140
|
+
};
|
|
141
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { findFirstMatch, getTypeInfo, loadProgram } from \"./core.js\";\nimport type { InferredTypeResult, Options } from \"./types.js\";\n\n// Re-export types\nexport type { Options, InferredTypeResult };\n\n// Re-export core utilities\nexport {\n\tfindFirstMatch,\n\tfindNearestTsconfig,\n\tgetTypeInfo,\n\tloadProgram,\n} from \"./core.js\";\n\n/**\n * Infer the type of a function or variable in a TypeScript file\n *\n * @param file - Path to the TypeScript file\n * @param name - Name of the function/variable to inspect\n * @param project - Optional path to tsconfig.json\n * @returns The inferred type information\n * @throws Error if file not found or symbol not found\n *\n * @example\n * ```ts\n * import { inferType } from \"prinfer\";\n *\n * const result = inferType(\"./src/utils.ts\", \"myFunction\");\n * console.log(result.signature);\n * // => \"(x: number, y: string) => boolean\"\n * console.log(result.returnType);\n * // => \"boolean\"\n * ```\n */\nexport function inferType(\n\tfile: string,\n\tname: string,\n\tproject?: string,\n): InferredTypeResult {\n\tconst entryFileAbs = path.resolve(process.cwd(), file);\n\n\tif (!fs.existsSync(entryFileAbs)) {\n\t\tthrow new Error(`File not found: ${entryFileAbs}`);\n\t}\n\n\tconst program = loadProgram(entryFileAbs, project);\n\tconst sourceFile = program.getSourceFile(entryFileAbs);\n\n\tif (!sourceFile) {\n\t\tthrow new Error(\n\t\t\t`Could not load source file into the program (check tsconfig include/exclude): ${entryFileAbs}`,\n\t\t);\n\t}\n\n\tconst node = findFirstMatch(sourceFile, name);\n\tif (!node) {\n\t\tthrow new Error(\n\t\t\t`No function-like symbol named \"${name}\" found in ${entryFileAbs}`,\n\t\t);\n\t}\n\n\treturn getTypeInfo(program, node);\n}\n\n/**\n * Infer the type using an options object\n *\n * @param options - Options for type inference\n * @returns The inferred type information\n */\nexport function inferTypeFromOptions(options: Options): InferredTypeResult {\n\treturn inferType(options.file, options.name, options.project);\n}\n","import path from \"node:path\";\nimport ts from \"typescript\";\nimport type { InferredTypeResult } from \"./types.js\";\n\n/**\n * Find the nearest tsconfig.json from a starting directory\n */\nexport function findNearestTsconfig(startDir: string): string | undefined {\n\treturn (\n\t\tts.findConfigFile(startDir, ts.sys.fileExists, \"tsconfig.json\") ??\n\t\tundefined\n\t);\n}\n\n/**\n * Load a TypeScript program from an entry file\n */\nexport function loadProgram(\n\tentryFileAbs: string,\n\tproject?: string,\n): ts.Program {\n\tconst fileDir = path.dirname(entryFileAbs);\n\tconst tsconfigPath = project\n\t\t? path.resolve(process.cwd(), project)\n\t\t: findNearestTsconfig(fileDir);\n\n\tif (!tsconfigPath) {\n\t\t// Fallback: single-file program\n\t\treturn ts.createProgram([entryFileAbs], {\n\t\t\ttarget: ts.ScriptTarget.ES2022,\n\t\t\tmodule: ts.ModuleKind.ESNext,\n\t\t\tstrict: true,\n\t\t\tallowJs: true,\n\t\t\tcheckJs: false,\n\t\t\tmoduleResolution: ts.ModuleResolutionKind.Bundler,\n\t\t\tskipLibCheck: true,\n\t\t});\n\t}\n\n\tconst cfg = ts.readConfigFile(tsconfigPath, ts.sys.readFile);\n\tif (cfg.error) {\n\t\tthrow new Error(\n\t\t\tts.flattenDiagnosticMessageText(cfg.error.messageText, \"\\n\"),\n\t\t);\n\t}\n\n\tconst parsed = ts.parseJsonConfigFileContent(\n\t\tcfg.config,\n\t\tts.sys,\n\t\tpath.dirname(tsconfigPath),\n\t);\n\treturn ts.createProgram({\n\t\trootNames: parsed.fileNames,\n\t\toptions: parsed.options,\n\t});\n}\n\nfunction isArrowOrFnExpr(\n\tn: ts.Node | undefined,\n): n is ts.ArrowFunction | ts.FunctionExpression {\n\treturn !!n && (ts.isArrowFunction(n) || ts.isFunctionExpression(n));\n}\n\nfunction nodeNameText(\n\tn: ts.PropertyName | ts.BindingName | undefined,\n): string | undefined {\n\tif (!n) return undefined;\n\tif (ts.isIdentifier(n)) return n.text;\n\tif (ts.isStringLiteral(n)) return n.text;\n\tif (ts.isNumericLiteral(n)) return n.text;\n\treturn undefined; // ignore computed names etc.\n}\n\nfunction isFunctionLikeNamed(node: ts.Node, name: string): boolean {\n\t// function foo() {}\n\tif (ts.isFunctionDeclaration(node) && node.name?.text === name) return true;\n\n\t// const foo = () => {} / function() {}\n\tif (\n\t\tts.isVariableDeclaration(node) &&\n\t\tts.isIdentifier(node.name) &&\n\t\tnode.name.text === name\n\t) {\n\t\treturn isArrowOrFnExpr(node.initializer);\n\t}\n\n\t// class C { foo() {} } / interface signatures\n\tif (\n\t\t(ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) &&\n\t\tnodeNameText(node.name) === name\n\t) {\n\t\treturn true;\n\t}\n\n\t// object literal { foo() {} } via MethodDeclaration inside object literal is also MethodDeclaration\n\t// property assignment: { foo: () => {} }\n\tif (ts.isPropertyAssignment(node) && nodeNameText(node.name) === name) {\n\t\treturn isArrowOrFnExpr(node.initializer);\n\t}\n\n\treturn false;\n}\n\n/**\n * Find the first function-like node with the given name in a source file\n */\nexport function findFirstMatch(\n\tsourceFile: ts.SourceFile,\n\tname: string,\n): ts.Node | undefined {\n\tlet found: ts.Node | undefined;\n\n\tconst visit = (node: ts.Node) => {\n\t\tif (found) return;\n\t\tif (isFunctionLikeNamed(node, name)) {\n\t\t\tfound = node;\n\t\t\treturn;\n\t\t}\n\t\tts.forEachChild(node, visit);\n\t};\n\n\tvisit(sourceFile);\n\treturn found;\n}\n\n/**\n * Get type information for a node\n */\nexport function getTypeInfo(\n\tprogram: ts.Program,\n\tnode: ts.Node,\n): InferredTypeResult {\n\tconst checker = program.getTypeChecker();\n\n\tlet sig: ts.Signature | undefined;\n\n\t// Prefer getting the signature from a declaration/expression directly\n\tif (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {\n\t\tsig = checker.getSignatureFromDeclaration(node) ?? undefined;\n\t} else if (ts.isVariableDeclaration(node)) {\n\t\tconst init = node.initializer;\n\t\tif (isArrowOrFnExpr(init))\n\t\t\tsig = checker.getSignatureFromDeclaration(init) ?? undefined;\n\t} else if (ts.isPropertyAssignment(node)) {\n\t\tconst init = node.initializer;\n\t\tif (isArrowOrFnExpr(init))\n\t\t\tsig = checker.getSignatureFromDeclaration(init) ?? undefined;\n\t} else if (ts.isMethodSignature(node)) {\n\t\t// interfaces/types\n\t\tsig = checker.getSignatureFromDeclaration(node) ?? undefined;\n\t}\n\n\tconst flags = ts.TypeFormatFlags.NoTruncation;\n\n\tif (sig) {\n\t\tconst signature = checker.signatureToString(sig, undefined, flags);\n\t\tconst ret = checker.getReturnTypeOfSignature(sig);\n\t\tconst returnType = checker.typeToString(ret, undefined, flags);\n\t\treturn { signature, returnType };\n\t}\n\n\t// Fallback: type at the name location\n\tconst nodeWithName = node as unknown as { name?: ts.Node };\n\tlet nameNode: ts.Node = node;\n\tif (nodeWithName.name && ts.isIdentifier(nodeWithName.name)) {\n\t\tnameNode = nodeWithName.name;\n\t}\n\n\tconst t = checker.getTypeAtLocation(nameNode);\n\treturn { signature: checker.typeToString(t, undefined, flags) };\n}\n"],"mappings":"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","names":["path","path"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "prinfer",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "TypeScript type inference inspection tool",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"bin": {
|
|
22
|
+
"prinfer": "./dist/cli.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "bun run tsup",
|
|
29
|
+
"test": "bun test",
|
|
30
|
+
"ci": "bun run build && bunx biome check . && bun test",
|
|
31
|
+
"changeset": "changeset",
|
|
32
|
+
"version": "changeset version",
|
|
33
|
+
"release": "bun run build && changeset publish",
|
|
34
|
+
"prepublishOnly": "bun run ci",
|
|
35
|
+
"prepare": "simple-git-hooks"
|
|
36
|
+
},
|
|
37
|
+
"simple-git-hooks": {
|
|
38
|
+
"pre-commit": "bunx biome check ."
|
|
39
|
+
},
|
|
40
|
+
"keywords": [
|
|
41
|
+
"typescript",
|
|
42
|
+
"type-inference",
|
|
43
|
+
"types",
|
|
44
|
+
"inspection",
|
|
45
|
+
"cli"
|
|
46
|
+
],
|
|
47
|
+
"author": "",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": ""
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"typescript": ">=4.7.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@changesets/cli": "^2.29.8",
|
|
58
|
+
"bun-types": "latest",
|
|
59
|
+
"simple-git-hooks": "^2.13.1",
|
|
60
|
+
"tsup": "^8.0.0",
|
|
61
|
+
"typescript": "^5.0.0"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"bun": ">=1.0.0"
|
|
65
|
+
}
|
|
66
|
+
}
|