prinfer 0.2.0 → 0.3.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/dist/cli.cjs +58 -17
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +58 -17
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +56 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -6
- package/dist/index.d.ts +25 -6
- package/dist/index.js +54 -8
- package/dist/index.js.map +1 -1
- package/dist/mcp.cjs +234 -0
- package/dist/mcp.cjs.map +1 -0
- package/dist/mcp.d.cts +1 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +211 -0
- package/dist/mcp.js.map +1 -0
- package/package.json +7 -2
package/dist/index.cjs.map
CHANGED
|
@@ -1 +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"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { findNodeByNameAndLine, 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\tfindNodeByNameAndLine,\n\tgetLineNumber,\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 options - Optional: project path (string) or options object with line and project\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 * // By name only\n * const result = inferType(\"./src/utils.ts\", \"myFunction\");\n * console.log(result.signature);\n * // => \"(x: number, y: string) => boolean\"\n *\n * // By name and line number\n * const result2 = inferType(\"./src/utils.ts\", \"commandResult\", { line: 75 });\n * console.log(result2.signature);\n * // => \"Result<VaultAction[], CommandError>\"\n * ```\n */\nexport function inferType(\n\tfile: string,\n\tname: string,\n\toptions?: string | { line?: number; project?: string },\n): InferredTypeResult {\n\t// Normalize options for backward compatibility\n\tconst opts =\n\t\ttypeof options === \"string\" ? { project: options } : (options ?? {});\n\tconst { line, project } = opts;\n\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 = findNodeByNameAndLine(sourceFile, name, line);\n\tif (!node) {\n\t\tconst lineInfo = line !== undefined ? ` at line ${line}` : \"\";\n\t\tthrow new Error(\n\t\t\t`No symbol named \"${name}\"${lineInfo} found in ${entryFileAbs}`,\n\t\t);\n\t}\n\n\treturn getTypeInfo(program, node, sourceFile);\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, {\n\t\tline: options.line,\n\t\tproject: options.project,\n\t});\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 * Check if a node is any variable declaration with the given name\n */\nfunction isVariableNamed(node: ts.Node, name: string): boolean {\n\tif (\n\t\tts.isVariableDeclaration(node) &&\n\t\tts.isIdentifier(node.name) &&\n\t\tnode.name.text === name\n\t) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/**\n * Check if a node matches the given name (function-like or variable)\n */\nfunction isNamedNode(node: ts.Node, name: string): boolean {\n\treturn isFunctionLikeNamed(node, name) || isVariableNamed(node, name);\n}\n\n/**\n * Get the 1-based line number for a node\n */\nexport function getLineNumber(\n\tsourceFile: ts.SourceFile,\n\tnode: ts.Node,\n): number {\n\tconst { line } = sourceFile.getLineAndCharacterOfPosition(\n\t\tnode.getStart(sourceFile),\n\t);\n\treturn line + 1;\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 * Find a node by name and optionally by line number\n */\nexport function findNodeByNameAndLine(\n\tsourceFile: ts.SourceFile,\n\tname: string,\n\tline?: number,\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 (isNamedNode(node, name)) {\n\t\t\tif (line !== undefined) {\n\t\t\t\tconst nodeLine = getLineNumber(sourceFile, node);\n\t\t\t\tif (nodeLine === line) {\n\t\t\t\t\tfound = node;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfound = node;\n\t\t\t\treturn;\n\t\t\t}\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\tsourceFile?: ts.SourceFile,\n): InferredTypeResult {\n\tconst checker = program.getTypeChecker();\n\tconst sf = sourceFile ?? node.getSourceFile();\n\tconst line = getLineNumber(sf, node);\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, line };\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), line };\n}\n"],"mappings":"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","names":["import_node_path","ts","path","path","fs"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -8,6 +8,8 @@ interface Options {
|
|
|
8
8
|
file: string;
|
|
9
9
|
/** Name of the function/variable to inspect */
|
|
10
10
|
name: string;
|
|
11
|
+
/** Optional line number to narrow search */
|
|
12
|
+
line?: number;
|
|
11
13
|
/** Optional path to tsconfig.json */
|
|
12
14
|
project?: string;
|
|
13
15
|
}
|
|
@@ -19,6 +21,8 @@ interface InferredTypeResult {
|
|
|
19
21
|
signature: string;
|
|
20
22
|
/** The return type (for functions) */
|
|
21
23
|
returnType?: string;
|
|
24
|
+
/** The line number where the symbol was found */
|
|
25
|
+
line?: number;
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
/**
|
|
@@ -29,21 +33,29 @@ declare function findNearestTsconfig(startDir: string): string | undefined;
|
|
|
29
33
|
* Load a TypeScript program from an entry file
|
|
30
34
|
*/
|
|
31
35
|
declare function loadProgram(entryFileAbs: string, project?: string): ts.Program;
|
|
36
|
+
/**
|
|
37
|
+
* Get the 1-based line number for a node
|
|
38
|
+
*/
|
|
39
|
+
declare function getLineNumber(sourceFile: ts.SourceFile, node: ts.Node): number;
|
|
32
40
|
/**
|
|
33
41
|
* Find the first function-like node with the given name in a source file
|
|
34
42
|
*/
|
|
35
43
|
declare function findFirstMatch(sourceFile: ts.SourceFile, name: string): ts.Node | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Find a node by name and optionally by line number
|
|
46
|
+
*/
|
|
47
|
+
declare function findNodeByNameAndLine(sourceFile: ts.SourceFile, name: string, line?: number): ts.Node | undefined;
|
|
36
48
|
/**
|
|
37
49
|
* Get type information for a node
|
|
38
50
|
*/
|
|
39
|
-
declare function getTypeInfo(program: ts.Program, node: ts.Node): InferredTypeResult;
|
|
51
|
+
declare function getTypeInfo(program: ts.Program, node: ts.Node, sourceFile?: ts.SourceFile): InferredTypeResult;
|
|
40
52
|
|
|
41
53
|
/**
|
|
42
54
|
* Infer the type of a function or variable in a TypeScript file
|
|
43
55
|
*
|
|
44
56
|
* @param file - Path to the TypeScript file
|
|
45
57
|
* @param name - Name of the function/variable to inspect
|
|
46
|
-
* @param
|
|
58
|
+
* @param options - Optional: project path (string) or options object with line and project
|
|
47
59
|
* @returns The inferred type information
|
|
48
60
|
* @throws Error if file not found or symbol not found
|
|
49
61
|
*
|
|
@@ -51,14 +63,21 @@ declare function getTypeInfo(program: ts.Program, node: ts.Node): InferredTypeRe
|
|
|
51
63
|
* ```ts
|
|
52
64
|
* import { inferType } from "prinfer";
|
|
53
65
|
*
|
|
66
|
+
* // By name only
|
|
54
67
|
* const result = inferType("./src/utils.ts", "myFunction");
|
|
55
68
|
* console.log(result.signature);
|
|
56
69
|
* // => "(x: number, y: string) => boolean"
|
|
57
|
-
*
|
|
58
|
-
* //
|
|
70
|
+
*
|
|
71
|
+
* // By name and line number
|
|
72
|
+
* const result2 = inferType("./src/utils.ts", "commandResult", { line: 75 });
|
|
73
|
+
* console.log(result2.signature);
|
|
74
|
+
* // => "Result<VaultAction[], CommandError>"
|
|
59
75
|
* ```
|
|
60
76
|
*/
|
|
61
|
-
declare function inferType(file: string, name: string,
|
|
77
|
+
declare function inferType(file: string, name: string, options?: string | {
|
|
78
|
+
line?: number;
|
|
79
|
+
project?: string;
|
|
80
|
+
}): InferredTypeResult;
|
|
62
81
|
/**
|
|
63
82
|
* Infer the type using an options object
|
|
64
83
|
*
|
|
@@ -67,4 +86,4 @@ declare function inferType(file: string, name: string, project?: string): Inferr
|
|
|
67
86
|
*/
|
|
68
87
|
declare function inferTypeFromOptions(options: Options): InferredTypeResult;
|
|
69
88
|
|
|
70
|
-
export { type InferredTypeResult, type Options, findFirstMatch, findNearestTsconfig, getTypeInfo, inferType, inferTypeFromOptions, loadProgram };
|
|
89
|
+
export { type InferredTypeResult, type Options, findFirstMatch, findNearestTsconfig, findNodeByNameAndLine, getLineNumber, getTypeInfo, inferType, inferTypeFromOptions, loadProgram };
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ interface Options {
|
|
|
8
8
|
file: string;
|
|
9
9
|
/** Name of the function/variable to inspect */
|
|
10
10
|
name: string;
|
|
11
|
+
/** Optional line number to narrow search */
|
|
12
|
+
line?: number;
|
|
11
13
|
/** Optional path to tsconfig.json */
|
|
12
14
|
project?: string;
|
|
13
15
|
}
|
|
@@ -19,6 +21,8 @@ interface InferredTypeResult {
|
|
|
19
21
|
signature: string;
|
|
20
22
|
/** The return type (for functions) */
|
|
21
23
|
returnType?: string;
|
|
24
|
+
/** The line number where the symbol was found */
|
|
25
|
+
line?: number;
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
/**
|
|
@@ -29,21 +33,29 @@ declare function findNearestTsconfig(startDir: string): string | undefined;
|
|
|
29
33
|
* Load a TypeScript program from an entry file
|
|
30
34
|
*/
|
|
31
35
|
declare function loadProgram(entryFileAbs: string, project?: string): ts.Program;
|
|
36
|
+
/**
|
|
37
|
+
* Get the 1-based line number for a node
|
|
38
|
+
*/
|
|
39
|
+
declare function getLineNumber(sourceFile: ts.SourceFile, node: ts.Node): number;
|
|
32
40
|
/**
|
|
33
41
|
* Find the first function-like node with the given name in a source file
|
|
34
42
|
*/
|
|
35
43
|
declare function findFirstMatch(sourceFile: ts.SourceFile, name: string): ts.Node | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Find a node by name and optionally by line number
|
|
46
|
+
*/
|
|
47
|
+
declare function findNodeByNameAndLine(sourceFile: ts.SourceFile, name: string, line?: number): ts.Node | undefined;
|
|
36
48
|
/**
|
|
37
49
|
* Get type information for a node
|
|
38
50
|
*/
|
|
39
|
-
declare function getTypeInfo(program: ts.Program, node: ts.Node): InferredTypeResult;
|
|
51
|
+
declare function getTypeInfo(program: ts.Program, node: ts.Node, sourceFile?: ts.SourceFile): InferredTypeResult;
|
|
40
52
|
|
|
41
53
|
/**
|
|
42
54
|
* Infer the type of a function or variable in a TypeScript file
|
|
43
55
|
*
|
|
44
56
|
* @param file - Path to the TypeScript file
|
|
45
57
|
* @param name - Name of the function/variable to inspect
|
|
46
|
-
* @param
|
|
58
|
+
* @param options - Optional: project path (string) or options object with line and project
|
|
47
59
|
* @returns The inferred type information
|
|
48
60
|
* @throws Error if file not found or symbol not found
|
|
49
61
|
*
|
|
@@ -51,14 +63,21 @@ declare function getTypeInfo(program: ts.Program, node: ts.Node): InferredTypeRe
|
|
|
51
63
|
* ```ts
|
|
52
64
|
* import { inferType } from "prinfer";
|
|
53
65
|
*
|
|
66
|
+
* // By name only
|
|
54
67
|
* const result = inferType("./src/utils.ts", "myFunction");
|
|
55
68
|
* console.log(result.signature);
|
|
56
69
|
* // => "(x: number, y: string) => boolean"
|
|
57
|
-
*
|
|
58
|
-
* //
|
|
70
|
+
*
|
|
71
|
+
* // By name and line number
|
|
72
|
+
* const result2 = inferType("./src/utils.ts", "commandResult", { line: 75 });
|
|
73
|
+
* console.log(result2.signature);
|
|
74
|
+
* // => "Result<VaultAction[], CommandError>"
|
|
59
75
|
* ```
|
|
60
76
|
*/
|
|
61
|
-
declare function inferType(file: string, name: string,
|
|
77
|
+
declare function inferType(file: string, name: string, options?: string | {
|
|
78
|
+
line?: number;
|
|
79
|
+
project?: string;
|
|
80
|
+
}): InferredTypeResult;
|
|
62
81
|
/**
|
|
63
82
|
* Infer the type using an options object
|
|
64
83
|
*
|
|
@@ -67,4 +86,4 @@ declare function inferType(file: string, name: string, project?: string): Inferr
|
|
|
67
86
|
*/
|
|
68
87
|
declare function inferTypeFromOptions(options: Options): InferredTypeResult;
|
|
69
88
|
|
|
70
|
-
export { type InferredTypeResult, type Options, findFirstMatch, findNearestTsconfig, getTypeInfo, inferType, inferTypeFromOptions, loadProgram };
|
|
89
|
+
export { type InferredTypeResult, type Options, findFirstMatch, findNearestTsconfig, findNodeByNameAndLine, getLineNumber, getTypeInfo, inferType, inferTypeFromOptions, loadProgram };
|
package/dist/index.js
CHANGED
|
@@ -61,6 +61,21 @@ function isFunctionLikeNamed(node, name) {
|
|
|
61
61
|
}
|
|
62
62
|
return false;
|
|
63
63
|
}
|
|
64
|
+
function isVariableNamed(node, name) {
|
|
65
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === name) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
function isNamedNode(node, name) {
|
|
71
|
+
return isFunctionLikeNamed(node, name) || isVariableNamed(node, name);
|
|
72
|
+
}
|
|
73
|
+
function getLineNumber(sourceFile, node) {
|
|
74
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(
|
|
75
|
+
node.getStart(sourceFile)
|
|
76
|
+
);
|
|
77
|
+
return line + 1;
|
|
78
|
+
}
|
|
64
79
|
function findFirstMatch(sourceFile, name) {
|
|
65
80
|
let found;
|
|
66
81
|
const visit = (node) => {
|
|
@@ -74,8 +89,31 @@ function findFirstMatch(sourceFile, name) {
|
|
|
74
89
|
visit(sourceFile);
|
|
75
90
|
return found;
|
|
76
91
|
}
|
|
77
|
-
function
|
|
92
|
+
function findNodeByNameAndLine(sourceFile, name, line) {
|
|
93
|
+
let found;
|
|
94
|
+
const visit = (node) => {
|
|
95
|
+
if (found) return;
|
|
96
|
+
if (isNamedNode(node, name)) {
|
|
97
|
+
if (line !== void 0) {
|
|
98
|
+
const nodeLine = getLineNumber(sourceFile, node);
|
|
99
|
+
if (nodeLine === line) {
|
|
100
|
+
found = node;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
found = node;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
ts.forEachChild(node, visit);
|
|
109
|
+
};
|
|
110
|
+
visit(sourceFile);
|
|
111
|
+
return found;
|
|
112
|
+
}
|
|
113
|
+
function getTypeInfo(program, node, sourceFile) {
|
|
78
114
|
const checker = program.getTypeChecker();
|
|
115
|
+
const sf = sourceFile ?? node.getSourceFile();
|
|
116
|
+
const line = getLineNumber(sf, node);
|
|
79
117
|
let sig;
|
|
80
118
|
if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {
|
|
81
119
|
sig = checker.getSignatureFromDeclaration(node) ?? void 0;
|
|
@@ -95,7 +133,7 @@ function getTypeInfo(program, node) {
|
|
|
95
133
|
const signature = checker.signatureToString(sig, void 0, flags);
|
|
96
134
|
const ret = checker.getReturnTypeOfSignature(sig);
|
|
97
135
|
const returnType = checker.typeToString(ret, void 0, flags);
|
|
98
|
-
return { signature, returnType };
|
|
136
|
+
return { signature, returnType, line };
|
|
99
137
|
}
|
|
100
138
|
const nodeWithName = node;
|
|
101
139
|
let nameNode = node;
|
|
@@ -103,11 +141,13 @@ function getTypeInfo(program, node) {
|
|
|
103
141
|
nameNode = nodeWithName.name;
|
|
104
142
|
}
|
|
105
143
|
const t = checker.getTypeAtLocation(nameNode);
|
|
106
|
-
return { signature: checker.typeToString(t, void 0, flags) };
|
|
144
|
+
return { signature: checker.typeToString(t, void 0, flags), line };
|
|
107
145
|
}
|
|
108
146
|
|
|
109
147
|
// src/index.ts
|
|
110
|
-
function inferType(file, name,
|
|
148
|
+
function inferType(file, name, options) {
|
|
149
|
+
const opts = typeof options === "string" ? { project: options } : options ?? {};
|
|
150
|
+
const { line, project } = opts;
|
|
111
151
|
const entryFileAbs = path2.resolve(process.cwd(), file);
|
|
112
152
|
if (!fs.existsSync(entryFileAbs)) {
|
|
113
153
|
throw new Error(`File not found: ${entryFileAbs}`);
|
|
@@ -119,20 +159,26 @@ function inferType(file, name, project) {
|
|
|
119
159
|
`Could not load source file into the program (check tsconfig include/exclude): ${entryFileAbs}`
|
|
120
160
|
);
|
|
121
161
|
}
|
|
122
|
-
const node =
|
|
162
|
+
const node = findNodeByNameAndLine(sourceFile, name, line);
|
|
123
163
|
if (!node) {
|
|
164
|
+
const lineInfo = line !== void 0 ? ` at line ${line}` : "";
|
|
124
165
|
throw new Error(
|
|
125
|
-
`No
|
|
166
|
+
`No symbol named "${name}"${lineInfo} found in ${entryFileAbs}`
|
|
126
167
|
);
|
|
127
168
|
}
|
|
128
|
-
return getTypeInfo(program, node);
|
|
169
|
+
return getTypeInfo(program, node, sourceFile);
|
|
129
170
|
}
|
|
130
171
|
function inferTypeFromOptions(options) {
|
|
131
|
-
return inferType(options.file, options.name,
|
|
172
|
+
return inferType(options.file, options.name, {
|
|
173
|
+
line: options.line,
|
|
174
|
+
project: options.project
|
|
175
|
+
});
|
|
132
176
|
}
|
|
133
177
|
export {
|
|
134
178
|
findFirstMatch,
|
|
135
179
|
findNearestTsconfig,
|
|
180
|
+
findNodeByNameAndLine,
|
|
181
|
+
getLineNumber,
|
|
136
182
|
getTypeInfo,
|
|
137
183
|
inferType,
|
|
138
184
|
inferTypeFromOptions,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { findNodeByNameAndLine, 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\tfindNodeByNameAndLine,\n\tgetLineNumber,\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 options - Optional: project path (string) or options object with line and project\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 * // By name only\n * const result = inferType(\"./src/utils.ts\", \"myFunction\");\n * console.log(result.signature);\n * // => \"(x: number, y: string) => boolean\"\n *\n * // By name and line number\n * const result2 = inferType(\"./src/utils.ts\", \"commandResult\", { line: 75 });\n * console.log(result2.signature);\n * // => \"Result<VaultAction[], CommandError>\"\n * ```\n */\nexport function inferType(\n\tfile: string,\n\tname: string,\n\toptions?: string | { line?: number; project?: string },\n): InferredTypeResult {\n\t// Normalize options for backward compatibility\n\tconst opts =\n\t\ttypeof options === \"string\" ? { project: options } : (options ?? {});\n\tconst { line, project } = opts;\n\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 = findNodeByNameAndLine(sourceFile, name, line);\n\tif (!node) {\n\t\tconst lineInfo = line !== undefined ? ` at line ${line}` : \"\";\n\t\tthrow new Error(\n\t\t\t`No symbol named \"${name}\"${lineInfo} found in ${entryFileAbs}`,\n\t\t);\n\t}\n\n\treturn getTypeInfo(program, node, sourceFile);\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, {\n\t\tline: options.line,\n\t\tproject: options.project,\n\t});\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 * Check if a node is any variable declaration with the given name\n */\nfunction isVariableNamed(node: ts.Node, name: string): boolean {\n\tif (\n\t\tts.isVariableDeclaration(node) &&\n\t\tts.isIdentifier(node.name) &&\n\t\tnode.name.text === name\n\t) {\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/**\n * Check if a node matches the given name (function-like or variable)\n */\nfunction isNamedNode(node: ts.Node, name: string): boolean {\n\treturn isFunctionLikeNamed(node, name) || isVariableNamed(node, name);\n}\n\n/**\n * Get the 1-based line number for a node\n */\nexport function getLineNumber(\n\tsourceFile: ts.SourceFile,\n\tnode: ts.Node,\n): number {\n\tconst { line } = sourceFile.getLineAndCharacterOfPosition(\n\t\tnode.getStart(sourceFile),\n\t);\n\treturn line + 1;\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 * Find a node by name and optionally by line number\n */\nexport function findNodeByNameAndLine(\n\tsourceFile: ts.SourceFile,\n\tname: string,\n\tline?: number,\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 (isNamedNode(node, name)) {\n\t\t\tif (line !== undefined) {\n\t\t\t\tconst nodeLine = getLineNumber(sourceFile, node);\n\t\t\t\tif (nodeLine === line) {\n\t\t\t\t\tfound = node;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfound = node;\n\t\t\t\treturn;\n\t\t\t}\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\tsourceFile?: ts.SourceFile,\n): InferredTypeResult {\n\tconst checker = program.getTypeChecker();\n\tconst sf = sourceFile ?? node.getSourceFile();\n\tconst line = getLineNumber(sf, node);\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, line };\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), line };\n}\n"],"mappings":"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","names":["path","path"]}
|
package/dist/mcp.cjs
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
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/mcp.ts
|
|
27
|
+
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
28
|
+
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
29
|
+
var import_zod = require("zod");
|
|
30
|
+
|
|
31
|
+
// src/index.ts
|
|
32
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
33
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
34
|
+
|
|
35
|
+
// src/core.ts
|
|
36
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
37
|
+
var import_typescript = __toESM(require("typescript"), 1);
|
|
38
|
+
function findNearestTsconfig(startDir) {
|
|
39
|
+
return import_typescript.default.findConfigFile(startDir, import_typescript.default.sys.fileExists, "tsconfig.json") ?? void 0;
|
|
40
|
+
}
|
|
41
|
+
function loadProgram(entryFileAbs, project) {
|
|
42
|
+
const fileDir = import_node_path.default.dirname(entryFileAbs);
|
|
43
|
+
const tsconfigPath = project ? import_node_path.default.resolve(process.cwd(), project) : findNearestTsconfig(fileDir);
|
|
44
|
+
if (!tsconfigPath) {
|
|
45
|
+
return import_typescript.default.createProgram([entryFileAbs], {
|
|
46
|
+
target: import_typescript.default.ScriptTarget.ES2022,
|
|
47
|
+
module: import_typescript.default.ModuleKind.ESNext,
|
|
48
|
+
strict: true,
|
|
49
|
+
allowJs: true,
|
|
50
|
+
checkJs: false,
|
|
51
|
+
moduleResolution: import_typescript.default.ModuleResolutionKind.Bundler,
|
|
52
|
+
skipLibCheck: true
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const cfg = import_typescript.default.readConfigFile(tsconfigPath, import_typescript.default.sys.readFile);
|
|
56
|
+
if (cfg.error) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
import_typescript.default.flattenDiagnosticMessageText(cfg.error.messageText, "\n")
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
const parsed = import_typescript.default.parseJsonConfigFileContent(
|
|
62
|
+
cfg.config,
|
|
63
|
+
import_typescript.default.sys,
|
|
64
|
+
import_node_path.default.dirname(tsconfigPath)
|
|
65
|
+
);
|
|
66
|
+
return import_typescript.default.createProgram({
|
|
67
|
+
rootNames: parsed.fileNames,
|
|
68
|
+
options: parsed.options
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function isArrowOrFnExpr(n) {
|
|
72
|
+
return !!n && (import_typescript.default.isArrowFunction(n) || import_typescript.default.isFunctionExpression(n));
|
|
73
|
+
}
|
|
74
|
+
function nodeNameText(n) {
|
|
75
|
+
if (!n) return void 0;
|
|
76
|
+
if (import_typescript.default.isIdentifier(n)) return n.text;
|
|
77
|
+
if (import_typescript.default.isStringLiteral(n)) return n.text;
|
|
78
|
+
if (import_typescript.default.isNumericLiteral(n)) return n.text;
|
|
79
|
+
return void 0;
|
|
80
|
+
}
|
|
81
|
+
function isFunctionLikeNamed(node, name) {
|
|
82
|
+
if (import_typescript.default.isFunctionDeclaration(node) && node.name?.text === name) return true;
|
|
83
|
+
if (import_typescript.default.isVariableDeclaration(node) && import_typescript.default.isIdentifier(node.name) && node.name.text === name) {
|
|
84
|
+
return isArrowOrFnExpr(node.initializer);
|
|
85
|
+
}
|
|
86
|
+
if ((import_typescript.default.isMethodDeclaration(node) || import_typescript.default.isMethodSignature(node)) && nodeNameText(node.name) === name) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
if (import_typescript.default.isPropertyAssignment(node) && nodeNameText(node.name) === name) {
|
|
90
|
+
return isArrowOrFnExpr(node.initializer);
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
function isVariableNamed(node, name) {
|
|
95
|
+
if (import_typescript.default.isVariableDeclaration(node) && import_typescript.default.isIdentifier(node.name) && node.name.text === name) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
function isNamedNode(node, name) {
|
|
101
|
+
return isFunctionLikeNamed(node, name) || isVariableNamed(node, name);
|
|
102
|
+
}
|
|
103
|
+
function getLineNumber(sourceFile, node) {
|
|
104
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(
|
|
105
|
+
node.getStart(sourceFile)
|
|
106
|
+
);
|
|
107
|
+
return line + 1;
|
|
108
|
+
}
|
|
109
|
+
function findNodeByNameAndLine(sourceFile, name, line) {
|
|
110
|
+
let found;
|
|
111
|
+
const visit = (node) => {
|
|
112
|
+
if (found) return;
|
|
113
|
+
if (isNamedNode(node, name)) {
|
|
114
|
+
if (line !== void 0) {
|
|
115
|
+
const nodeLine = getLineNumber(sourceFile, node);
|
|
116
|
+
if (nodeLine === line) {
|
|
117
|
+
found = node;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
found = node;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
import_typescript.default.forEachChild(node, visit);
|
|
126
|
+
};
|
|
127
|
+
visit(sourceFile);
|
|
128
|
+
return found;
|
|
129
|
+
}
|
|
130
|
+
function getTypeInfo(program, node, sourceFile) {
|
|
131
|
+
const checker = program.getTypeChecker();
|
|
132
|
+
const sf = sourceFile ?? node.getSourceFile();
|
|
133
|
+
const line = getLineNumber(sf, node);
|
|
134
|
+
let sig;
|
|
135
|
+
if (import_typescript.default.isFunctionDeclaration(node) || import_typescript.default.isMethodDeclaration(node)) {
|
|
136
|
+
sig = checker.getSignatureFromDeclaration(node) ?? void 0;
|
|
137
|
+
} else if (import_typescript.default.isVariableDeclaration(node)) {
|
|
138
|
+
const init = node.initializer;
|
|
139
|
+
if (isArrowOrFnExpr(init))
|
|
140
|
+
sig = checker.getSignatureFromDeclaration(init) ?? void 0;
|
|
141
|
+
} else if (import_typescript.default.isPropertyAssignment(node)) {
|
|
142
|
+
const init = node.initializer;
|
|
143
|
+
if (isArrowOrFnExpr(init))
|
|
144
|
+
sig = checker.getSignatureFromDeclaration(init) ?? void 0;
|
|
145
|
+
} else if (import_typescript.default.isMethodSignature(node)) {
|
|
146
|
+
sig = checker.getSignatureFromDeclaration(node) ?? void 0;
|
|
147
|
+
}
|
|
148
|
+
const flags = import_typescript.default.TypeFormatFlags.NoTruncation;
|
|
149
|
+
if (sig) {
|
|
150
|
+
const signature = checker.signatureToString(sig, void 0, flags);
|
|
151
|
+
const ret = checker.getReturnTypeOfSignature(sig);
|
|
152
|
+
const returnType = checker.typeToString(ret, void 0, flags);
|
|
153
|
+
return { signature, returnType, line };
|
|
154
|
+
}
|
|
155
|
+
const nodeWithName = node;
|
|
156
|
+
let nameNode = node;
|
|
157
|
+
if (nodeWithName.name && import_typescript.default.isIdentifier(nodeWithName.name)) {
|
|
158
|
+
nameNode = nodeWithName.name;
|
|
159
|
+
}
|
|
160
|
+
const t = checker.getTypeAtLocation(nameNode);
|
|
161
|
+
return { signature: checker.typeToString(t, void 0, flags), line };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/index.ts
|
|
165
|
+
function inferType(file, name, options) {
|
|
166
|
+
const opts = typeof options === "string" ? { project: options } : options ?? {};
|
|
167
|
+
const { line, project } = opts;
|
|
168
|
+
const entryFileAbs = import_node_path2.default.resolve(process.cwd(), file);
|
|
169
|
+
if (!import_node_fs.default.existsSync(entryFileAbs)) {
|
|
170
|
+
throw new Error(`File not found: ${entryFileAbs}`);
|
|
171
|
+
}
|
|
172
|
+
const program = loadProgram(entryFileAbs, project);
|
|
173
|
+
const sourceFile = program.getSourceFile(entryFileAbs);
|
|
174
|
+
if (!sourceFile) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
`Could not load source file into the program (check tsconfig include/exclude): ${entryFileAbs}`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
const node = findNodeByNameAndLine(sourceFile, name, line);
|
|
180
|
+
if (!node) {
|
|
181
|
+
const lineInfo = line !== void 0 ? ` at line ${line}` : "";
|
|
182
|
+
throw new Error(
|
|
183
|
+
`No symbol named "${name}"${lineInfo} found in ${entryFileAbs}`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
return getTypeInfo(program, node, sourceFile);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/mcp.ts
|
|
190
|
+
var server = new import_mcp.McpServer({
|
|
191
|
+
name: "prinfer",
|
|
192
|
+
version: "0.2.1"
|
|
193
|
+
});
|
|
194
|
+
server.tool(
|
|
195
|
+
"infer_type",
|
|
196
|
+
"Infer the TypeScript type of a function or variable in a file. Returns the type signature and optionally the return type for functions.",
|
|
197
|
+
{
|
|
198
|
+
file: import_zod.z.string().describe("Path to the TypeScript file"),
|
|
199
|
+
name: import_zod.z.string().describe("Name of the function or variable to inspect"),
|
|
200
|
+
line: import_zod.z.number().optional().describe("Optional line number to narrow search (1-based)"),
|
|
201
|
+
project: import_zod.z.string().optional().describe("Optional path to tsconfig.json")
|
|
202
|
+
},
|
|
203
|
+
async ({ file, name, line, project }) => {
|
|
204
|
+
try {
|
|
205
|
+
const result = inferType(file, name, { line, project });
|
|
206
|
+
let text = `Type: ${result.signature}`;
|
|
207
|
+
if (result.returnType) {
|
|
208
|
+
text += `
|
|
209
|
+
Returns: ${result.returnType}`;
|
|
210
|
+
}
|
|
211
|
+
if (result.line) {
|
|
212
|
+
text += `
|
|
213
|
+
Line: ${result.line}`;
|
|
214
|
+
}
|
|
215
|
+
return { content: [{ type: "text", text }] };
|
|
216
|
+
} catch (error) {
|
|
217
|
+
return {
|
|
218
|
+
content: [
|
|
219
|
+
{
|
|
220
|
+
type: "text",
|
|
221
|
+
text: `Error: ${error.message}`
|
|
222
|
+
}
|
|
223
|
+
],
|
|
224
|
+
isError: true
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
);
|
|
229
|
+
async function main() {
|
|
230
|
+
const transport = new import_stdio.StdioServerTransport();
|
|
231
|
+
await server.connect(transport);
|
|
232
|
+
}
|
|
233
|
+
main().catch(console.error);
|
|
234
|
+
//# sourceMappingURL=mcp.cjs.map
|