@rockcarver/frodo-cli 4.0.0-26 → 4.0.0-28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -1
- package/dist/app.cjs +5 -1
- package/dist/app.cjs.map +1 -1
- package/package.json +1 -1
- package/tmp_frodo_probe.js +0 -160
package/package.json
CHANGED
package/tmp_frodo_probe.js
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
const repl = require('node:repl');
|
|
2
|
-
const { frodo } = require('@rockcarver/frodo-lib');
|
|
3
|
-
|
|
4
|
-
function getHelpMetadataFromInstance(frodoInstance) {
|
|
5
|
-
const candidate = frodoInstance['utils'];
|
|
6
|
-
if (!candidate || typeof candidate !== 'object') return [];
|
|
7
|
-
const getHelpMetadata = candidate['getHelpMetadata'];
|
|
8
|
-
if (typeof getHelpMetadata !== 'function') return [];
|
|
9
|
-
const result = getHelpMetadata();
|
|
10
|
-
return Array.isArray(result) ? result : [];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function getValueAtPath(root, pathSegments) {
|
|
14
|
-
let current = root;
|
|
15
|
-
for (const segment of pathSegments) {
|
|
16
|
-
if (!current || typeof current !== 'object') return undefined;
|
|
17
|
-
current = current[segment];
|
|
18
|
-
}
|
|
19
|
-
return current;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function normalizeName(value) {
|
|
23
|
-
return value.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function findBestDoc(methodName, moduleSegments, docsByMethod) {
|
|
27
|
-
const docs = docsByMethod.get(methodName) ?? [];
|
|
28
|
-
if (docs.length === 0) return undefined;
|
|
29
|
-
const normalizedModuleSegments = moduleSegments.map(normalizeName);
|
|
30
|
-
const exactMatch = docs.find((d) =>
|
|
31
|
-
normalizedModuleSegments.includes(normalizeName(d.typeName))
|
|
32
|
-
);
|
|
33
|
-
return exactMatch ?? docs[0];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function buildMethodScaffold(moduleSegments, methodName, docsByMethod) {
|
|
37
|
-
const doc = findBestDoc(methodName, moduleSegments, docsByMethod);
|
|
38
|
-
if (!doc) return '()';
|
|
39
|
-
const paramNames = (doc.params ?? [])
|
|
40
|
-
.map((p) => p.name.trim())
|
|
41
|
-
.filter((name) => name.length > 0);
|
|
42
|
-
if (paramNames.length === 0) return '()';
|
|
43
|
-
return `(${paramNames.join(', ')})`;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function abbrevType(type) {
|
|
47
|
-
const t = type.trim();
|
|
48
|
-
if (!t) return t;
|
|
49
|
-
if (t.includes('=>') || t.startsWith('(')) return 'fn';
|
|
50
|
-
if (t.endsWith('[]')) return abbrevType(t.slice(0, -2)) + '[]';
|
|
51
|
-
switch (t) {
|
|
52
|
-
case 'string':
|
|
53
|
-
return 'str';
|
|
54
|
-
case 'boolean':
|
|
55
|
-
return 'bool';
|
|
56
|
-
case 'number':
|
|
57
|
-
return 'num';
|
|
58
|
-
default:
|
|
59
|
-
return t;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function buildHintLine(fullPath, doc) {
|
|
64
|
-
const params = (doc.params ?? [])
|
|
65
|
-
.map((p) => {
|
|
66
|
-
const name = p.name.trim();
|
|
67
|
-
const type = abbrevType(p.type);
|
|
68
|
-
return name && type ? `${name}: ${type}` : name;
|
|
69
|
-
})
|
|
70
|
-
.filter((s) => s.length > 0)
|
|
71
|
-
.join(', ');
|
|
72
|
-
return `// ${fullPath}(${params})`;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function buildDocsByMethod(frodoInstance) {
|
|
76
|
-
const helpDocs = getHelpMetadataFromInstance(frodoInstance);
|
|
77
|
-
const docsByMethod = new Map();
|
|
78
|
-
for (const doc of helpDocs) {
|
|
79
|
-
if (!docsByMethod.has(doc.methodName)) docsByMethod.set(doc.methodName, []);
|
|
80
|
-
docsByMethod.get(doc.methodName).push(doc);
|
|
81
|
-
}
|
|
82
|
-
return docsByMethod;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function createFrodoCompleter(rootBindings, docsByMethod, onMethodHint) {
|
|
86
|
-
return (line) => {
|
|
87
|
-
const tokenMatch = line.match(/([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*\.?$)/);
|
|
88
|
-
const token = tokenMatch?.[1] ?? '';
|
|
89
|
-
if (!token) return [[], line];
|
|
90
|
-
|
|
91
|
-
const rootCandidates = Object.keys(rootBindings).filter((rootName) =>
|
|
92
|
-
rootName.startsWith(token)
|
|
93
|
-
);
|
|
94
|
-
if (!token.includes('.')) {
|
|
95
|
-
const rootCompletions = rootCandidates.sort((a, b) => a.localeCompare(b));
|
|
96
|
-
return [rootCompletions, token];
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const segments = token.split('.');
|
|
100
|
-
const rootName = segments[0];
|
|
101
|
-
const root = rootBindings[rootName];
|
|
102
|
-
if (!root) return [[], token];
|
|
103
|
-
|
|
104
|
-
const hasTrailingDot = token.endsWith('.');
|
|
105
|
-
const parentSegments = hasTrailingDot ? segments.slice(1, -1) : segments.slice(1, -1);
|
|
106
|
-
const partial = hasTrailingDot ? '' : segments[segments.length - 1];
|
|
107
|
-
|
|
108
|
-
const targetObj = getValueAtPath(root, parentSegments);
|
|
109
|
-
if (!targetObj || typeof targetObj !== 'object') return [[], token];
|
|
110
|
-
|
|
111
|
-
const completions = Object.keys(targetObj)
|
|
112
|
-
.filter((k) => k.startsWith(partial))
|
|
113
|
-
.sort((a, b) => a.localeCompare(b))
|
|
114
|
-
.map((k) => {
|
|
115
|
-
const value = targetObj[k];
|
|
116
|
-
const prefix = `${rootName}.${[...parentSegments, k].join('.')}`;
|
|
117
|
-
if (typeof value !== 'function') return prefix;
|
|
118
|
-
return `${prefix}${buildMethodScaffold(parentSegments, k, docsByMethod)}`;
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
if (onMethodHint && completions.length === 1) {
|
|
122
|
-
const completion = completions[0];
|
|
123
|
-
const parenIdx = completion.indexOf('(');
|
|
124
|
-
if (parenIdx !== -1) {
|
|
125
|
-
const dotIdx = completion.lastIndexOf('.', parenIdx);
|
|
126
|
-
const resolvedMethodName = completion.slice(dotIdx + 1, parenIdx);
|
|
127
|
-
const doc = findBestDoc(resolvedMethodName, parentSegments, docsByMethod);
|
|
128
|
-
if (doc) {
|
|
129
|
-
const fullPath = completion.slice(0, parenIdx);
|
|
130
|
-
setImmediate(() => onMethodHint(buildHintLine(fullPath, doc)));
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return [completions, token];
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function printHintAbovePrompt(replServer, hint) {
|
|
140
|
-
if (!replServer) return;
|
|
141
|
-
setImmediate(() => {
|
|
142
|
-
process.stdout.write(`\n${hint}\n`);
|
|
143
|
-
const refreshLine = replServer._refreshLine;
|
|
144
|
-
if (typeof refreshLine === 'function') {
|
|
145
|
-
refreshLine.call(replServer);
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
replServer.displayPrompt(true);
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const docsByMethod = buildDocsByMethod(frodo);
|
|
153
|
-
const rootBindings = { frodo, frodoLib: frodo };
|
|
154
|
-
let rs;
|
|
155
|
-
const completer = createFrodoCompleter(rootBindings, docsByMethod, (hint) => {
|
|
156
|
-
printHintAbovePrompt(rs, hint);
|
|
157
|
-
});
|
|
158
|
-
rs = repl.start({ prompt: '> ', completer, useGlobal: true });
|
|
159
|
-
rs.context.frodo = frodo;
|
|
160
|
-
rs.context.frodoLib = frodo;
|