@tsrx/mcp 0.0.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 +115 -0
- package/package.json +51 -0
- package/src/analyze.js +195 -0
- package/src/compile.js +235 -0
- package/src/docs.js +50 -0
- package/src/format.js +138 -0
- package/src/generated/docs.js +79 -0
- package/src/http.js +99 -0
- package/src/index.js +25 -0
- package/src/inspect.js +235 -0
- package/src/schemas.js +141 -0
- package/src/server.js +629 -0
- package/src/stdio.js +8 -0
- package/src/target.js +216 -0
- package/src/validate.js +112 -0
package/src/target.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
export const TARGET_CANDIDATES = [
|
|
5
|
+
{
|
|
6
|
+
target: 'ripple',
|
|
7
|
+
compilerPackage: '@tsrx/ripple',
|
|
8
|
+
signals: ['@tsrx/ripple', 'ripple', '@ripple-ts/vite-plugin', '@ripple-ts/compat-react'],
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
target: 'react',
|
|
12
|
+
compilerPackage: '@tsrx/react',
|
|
13
|
+
signals: [
|
|
14
|
+
'@tsrx/react',
|
|
15
|
+
'@tsrx/vite-plugin-react',
|
|
16
|
+
'@tsrx/rspack-plugin-react',
|
|
17
|
+
'@tsrx/turbopack-plugin-react',
|
|
18
|
+
'@tsrx/bun-plugin-react',
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
target: 'preact',
|
|
23
|
+
compilerPackage: '@tsrx/preact',
|
|
24
|
+
signals: [
|
|
25
|
+
'@tsrx/preact',
|
|
26
|
+
'@tsrx/vite-plugin-preact',
|
|
27
|
+
'@tsrx/rspack-plugin-preact',
|
|
28
|
+
'@tsrx/bun-plugin-preact',
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
target: 'solid',
|
|
33
|
+
compilerPackage: '@tsrx/solid',
|
|
34
|
+
signals: ['@tsrx/solid', '@tsrx/vite-plugin-solid', '@tsrx/rspack-plugin-solid'],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
target: 'vue',
|
|
38
|
+
compilerPackage: '@tsrx/vue',
|
|
39
|
+
signals: ['@tsrx/vue', '@tsrx/vite-plugin-vue', '@tsrx/rspack-plugin-vue'],
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
export const CONFIG_FILES = [
|
|
44
|
+
'vite.config.js',
|
|
45
|
+
'vite.config.ts',
|
|
46
|
+
'vite.config.mjs',
|
|
47
|
+
'vite.config.mts',
|
|
48
|
+
'rspack.config.js',
|
|
49
|
+
'rspack.config.ts',
|
|
50
|
+
'next.config.js',
|
|
51
|
+
'next.config.mjs',
|
|
52
|
+
'next.config.ts',
|
|
53
|
+
'tsconfig.json',
|
|
54
|
+
'prettier.config.js',
|
|
55
|
+
'prettier.config.mjs',
|
|
56
|
+
'prettier.config.cjs',
|
|
57
|
+
'.prettierrc',
|
|
58
|
+
'eslint.config.js',
|
|
59
|
+
'eslint.config.mjs',
|
|
60
|
+
'eslint.config.cjs',
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {string | undefined} [cwd]
|
|
65
|
+
*/
|
|
66
|
+
export function resolve_cwd(cwd) {
|
|
67
|
+
return path.resolve(cwd ?? process.cwd());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param {string | undefined} [cwd]
|
|
72
|
+
*/
|
|
73
|
+
export function resolve_cwd_context(cwd) {
|
|
74
|
+
const cwd_specified = cwd !== undefined;
|
|
75
|
+
const resolved_cwd = resolve_cwd(cwd);
|
|
76
|
+
return {
|
|
77
|
+
cwd: resolved_cwd,
|
|
78
|
+
hint: cwd_hint(resolved_cwd, cwd_specified) || null,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {string} start
|
|
84
|
+
*/
|
|
85
|
+
export function find_package_json(start) {
|
|
86
|
+
let current = path.resolve(start);
|
|
87
|
+
for (;;) {
|
|
88
|
+
const candidate = path.join(current, 'package.json');
|
|
89
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
90
|
+
const parent = path.dirname(current);
|
|
91
|
+
if (parent === current) return null;
|
|
92
|
+
current = parent;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @param {Record<string, unknown>} package_json
|
|
98
|
+
*/
|
|
99
|
+
function get_dependency_names(package_json) {
|
|
100
|
+
const names = new Set();
|
|
101
|
+
for (const field of [
|
|
102
|
+
'dependencies',
|
|
103
|
+
'devDependencies',
|
|
104
|
+
'peerDependencies',
|
|
105
|
+
'optionalDependencies',
|
|
106
|
+
]) {
|
|
107
|
+
const deps = package_json[field];
|
|
108
|
+
if (deps && typeof deps === 'object') {
|
|
109
|
+
for (const name of Object.keys(deps)) names.add(name);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return names;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @param {string} root
|
|
117
|
+
*/
|
|
118
|
+
function read_config_text(root) {
|
|
119
|
+
let text = '';
|
|
120
|
+
for (const file of CONFIG_FILES) {
|
|
121
|
+
const filename = path.join(root, file);
|
|
122
|
+
if (fs.existsSync(filename)) {
|
|
123
|
+
try {
|
|
124
|
+
text += `\n${fs.readFileSync(filename, 'utf8')}`;
|
|
125
|
+
} catch {
|
|
126
|
+
// Best-effort context only.
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return text;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @param {string} resolved_cwd
|
|
135
|
+
* @param {boolean} cwd_specified
|
|
136
|
+
*/
|
|
137
|
+
function cwd_hint(resolved_cwd, cwd_specified) {
|
|
138
|
+
if (cwd_specified) return '';
|
|
139
|
+
return ` Hint: cwd was not supplied, so this defaulted to ${resolved_cwd}. Pass cwd pointing at your project root for accurate target detection.`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @param {string | undefined} [cwd]
|
|
144
|
+
*/
|
|
145
|
+
export function detect_target(cwd) {
|
|
146
|
+
const cwd_context = resolve_cwd_context(cwd);
|
|
147
|
+
const package_json_path = find_package_json(cwd_context.cwd);
|
|
148
|
+
if (!package_json_path) {
|
|
149
|
+
return {
|
|
150
|
+
cwd: cwd_context.cwd,
|
|
151
|
+
packageJsonPath: null,
|
|
152
|
+
detectedTarget: null,
|
|
153
|
+
confidence: /** @type {'none'} */ ('none'),
|
|
154
|
+
matches: [],
|
|
155
|
+
message:
|
|
156
|
+
'No package.json found from the supplied cwd or its ancestors.' + (cwd_context.hint ?? ''),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const root = path.dirname(package_json_path);
|
|
161
|
+
/** @type {Record<string, unknown>} */
|
|
162
|
+
let package_json;
|
|
163
|
+
try {
|
|
164
|
+
package_json = JSON.parse(fs.readFileSync(package_json_path, 'utf8'));
|
|
165
|
+
} catch (error) {
|
|
166
|
+
return {
|
|
167
|
+
cwd: cwd_context.cwd,
|
|
168
|
+
packageJsonPath: package_json_path,
|
|
169
|
+
detectedTarget: null,
|
|
170
|
+
confidence: 'none',
|
|
171
|
+
matches: [],
|
|
172
|
+
message: `Could not parse package.json: ${error instanceof Error ? error.message : String(error)}`,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const dependency_names = get_dependency_names(package_json);
|
|
177
|
+
const config_text = read_config_text(root);
|
|
178
|
+
const matches = [];
|
|
179
|
+
|
|
180
|
+
for (const candidate of TARGET_CANDIDATES) {
|
|
181
|
+
const matched_signals = candidate.signals.filter(
|
|
182
|
+
(signal) => dependency_names.has(signal) || config_text.includes(signal),
|
|
183
|
+
);
|
|
184
|
+
if (matched_signals.length > 0) {
|
|
185
|
+
matches.push({
|
|
186
|
+
target: candidate.target,
|
|
187
|
+
compilerPackage: candidate.compilerPackage,
|
|
188
|
+
signals: matched_signals,
|
|
189
|
+
score: matched_signals.length,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
matches.sort((a, b) => b.score - a.score || a.target.localeCompare(b.target));
|
|
195
|
+
const detected = matches[0] ?? null;
|
|
196
|
+
const tied = detected ? matches.filter((match) => match.score === detected.score) : [];
|
|
197
|
+
const detected_target = tied.length === 1 ? detected.target : null;
|
|
198
|
+
|
|
199
|
+
const base_message =
|
|
200
|
+
tied.length > 1
|
|
201
|
+
? `Multiple TSRX targets matched equally: ${tied.map((match) => match.target).join(', ')}.`
|
|
202
|
+
: detected
|
|
203
|
+
? `Detected TSRX target "${detected.target}" from ${detected.signals.join(', ')}.`
|
|
204
|
+
: 'No TSRX target packages were found in package.json or common bundler configs.';
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
cwd: cwd_context.cwd,
|
|
208
|
+
packageJsonPath: package_json_path,
|
|
209
|
+
detectedTarget: detected_target,
|
|
210
|
+
confidence: /** @type {'high' | 'ambiguous' | 'none'} */ (
|
|
211
|
+
detected ? (tied.length === 1 ? 'high' : 'ambiguous') : 'none'
|
|
212
|
+
),
|
|
213
|
+
matches,
|
|
214
|
+
message: base_message + (cwd_context.hint ?? ''),
|
|
215
|
+
};
|
|
216
|
+
}
|
package/src/validate.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { analyze_tsrx_result } from './analyze.js';
|
|
4
|
+
import { compile_tsrx } from './compile.js';
|
|
5
|
+
import { format_tsrx } from './format.js';
|
|
6
|
+
import { resolve_cwd_context } from './target.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {unknown} error
|
|
10
|
+
*/
|
|
11
|
+
function normalize_read_error(error) {
|
|
12
|
+
if (error && typeof error === 'object') {
|
|
13
|
+
const candidate = /** @type {Record<string, unknown>} */ (error);
|
|
14
|
+
return {
|
|
15
|
+
message: candidate.message ? String(candidate.message) : String(error),
|
|
16
|
+
code: candidate.code ? String(candidate.code) : null,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
message: String(error),
|
|
21
|
+
code: null,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} filePath
|
|
27
|
+
* @param {string} cwd
|
|
28
|
+
*/
|
|
29
|
+
function resolve_file_path(filePath, cwd) {
|
|
30
|
+
return path.isAbsolute(filePath) ? path.resolve(filePath) : path.resolve(cwd, filePath);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {{
|
|
35
|
+
* filePath: string,
|
|
36
|
+
* cwd?: string,
|
|
37
|
+
* target?: string,
|
|
38
|
+
* collect?: boolean,
|
|
39
|
+
* loose?: boolean,
|
|
40
|
+
* mode?: 'client' | 'server',
|
|
41
|
+
* printWidth?: number,
|
|
42
|
+
* tabWidth?: number,
|
|
43
|
+
* useTabs?: boolean,
|
|
44
|
+
* singleQuote?: boolean,
|
|
45
|
+
* }} input
|
|
46
|
+
*/
|
|
47
|
+
export async function validate_tsrx_file(input) {
|
|
48
|
+
const cwd_context = resolve_cwd_context(input.cwd);
|
|
49
|
+
const cwd = cwd_context.cwd;
|
|
50
|
+
const filePath = resolve_file_path(input.filePath, cwd);
|
|
51
|
+
const filename = filePath;
|
|
52
|
+
|
|
53
|
+
let code;
|
|
54
|
+
try {
|
|
55
|
+
code = await fs.readFile(filePath, 'utf8');
|
|
56
|
+
} catch (error) {
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
cwd,
|
|
60
|
+
filePath,
|
|
61
|
+
filename,
|
|
62
|
+
message: cwd_context.hint,
|
|
63
|
+
read: {
|
|
64
|
+
ok: false,
|
|
65
|
+
error: normalize_read_error(error),
|
|
66
|
+
},
|
|
67
|
+
format: null,
|
|
68
|
+
compile: null,
|
|
69
|
+
analysis: null,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const formatResult = await format_tsrx({
|
|
74
|
+
code,
|
|
75
|
+
filename,
|
|
76
|
+
cwd,
|
|
77
|
+
printWidth: input.printWidth,
|
|
78
|
+
tabWidth: input.tabWidth,
|
|
79
|
+
useTabs: input.useTabs,
|
|
80
|
+
singleQuote: input.singleQuote,
|
|
81
|
+
check: true,
|
|
82
|
+
});
|
|
83
|
+
const compileResult = await compile_tsrx({
|
|
84
|
+
code,
|
|
85
|
+
filename,
|
|
86
|
+
cwd,
|
|
87
|
+
target: input.target,
|
|
88
|
+
collect: input.collect,
|
|
89
|
+
loose: input.loose,
|
|
90
|
+
mode: input.mode,
|
|
91
|
+
includeCode: false,
|
|
92
|
+
});
|
|
93
|
+
const analysisResult = analyze_tsrx_result({
|
|
94
|
+
code,
|
|
95
|
+
compileResult,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
ok: formatResult.ok && formatResult.check === true && compileResult.ok,
|
|
100
|
+
cwd,
|
|
101
|
+
filePath,
|
|
102
|
+
filename,
|
|
103
|
+
message: cwd_context.hint,
|
|
104
|
+
read: {
|
|
105
|
+
ok: true,
|
|
106
|
+
error: null,
|
|
107
|
+
},
|
|
108
|
+
format: formatResult,
|
|
109
|
+
compile: compileResult,
|
|
110
|
+
analysis: analysisResult,
|
|
111
|
+
};
|
|
112
|
+
}
|