@kaluchi/jdtbridge 1.1.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/README.md +102 -0
- package/bin/jdt.mjs +3 -0
- package/package.json +44 -0
- package/src/args.mjs +37 -0
- package/src/cli.mjs +174 -0
- package/src/client.mjs +168 -0
- package/src/color.mjs +40 -0
- package/src/commands/build.mjs +40 -0
- package/src/commands/editor.mjs +49 -0
- package/src/commands/errors.mjs +52 -0
- package/src/commands/find.mjs +41 -0
- package/src/commands/hierarchy.mjs +48 -0
- package/src/commands/implementors.mjs +34 -0
- package/src/commands/project-info.mjs +45 -0
- package/src/commands/projects.mjs +16 -0
- package/src/commands/refactoring.mjs +116 -0
- package/src/commands/references.mjs +51 -0
- package/src/commands/setup.mjs +383 -0
- package/src/commands/source.mjs +43 -0
- package/src/commands/subtypes.mjs +33 -0
- package/src/commands/test.mjs +47 -0
- package/src/commands/type-info.mjs +47 -0
- package/src/discovery.mjs +82 -0
- package/src/eclipse.mjs +324 -0
- package/src/format/project-info.mjs +118 -0
- package/src/format/references.mjs +28 -0
- package/src/format/test-results.mjs +31 -0
- package/src/home.mjs +55 -0
- package/src/paths.mjs +17 -0
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# @kaluchi/jdtbridge — CLI reference
|
|
2
|
+
|
|
3
|
+
CLI for [JDT Bridge](../README.md). Requires Eclipse running with the jdtbridge plugin installed.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd cli
|
|
9
|
+
npm install
|
|
10
|
+
npm link # registers `jdt` and `jdtbridge` global commands
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Plugin setup
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
jdt setup # build + install into Eclipse
|
|
17
|
+
jdt setup --check # diagnostic: show status of all components
|
|
18
|
+
jdt setup --skip-build # reinstall last build
|
|
19
|
+
jdt setup --clean # clean build (mvn clean verify)
|
|
20
|
+
jdt setup --remove # uninstall plugin from Eclipse
|
|
21
|
+
jdt setup --eclipse <path> # specify Eclipse path (saved to config)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
If Eclipse is running, you will be prompted to stop it. After install, Eclipse restarts automatically with the same workspace.
|
|
25
|
+
|
|
26
|
+
## Commands
|
|
27
|
+
|
|
28
|
+
Run `jdt help <command>` for detailed flags and examples. Most commands have short aliases.
|
|
29
|
+
|
|
30
|
+
### Search & navigation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
jdt projects # list workspace projects
|
|
34
|
+
jdt project-info <name> [--lines N] # (alias: pi) project overview
|
|
35
|
+
jdt find <Name> [--source-only] # find type declarations (* wildcards)
|
|
36
|
+
jdt references <FQN> [method] [--field <name>] # (alias: refs) references to type/method/field
|
|
37
|
+
jdt subtypes <FQN> # (alias: subt) all subtypes/implementors
|
|
38
|
+
jdt hierarchy <FQN> # (alias: hier) supers + interfaces + subtypes
|
|
39
|
+
jdt implementors <FQN> <method> [--arity N] # (alias: impl) implementations of interface method
|
|
40
|
+
jdt type-info <FQN> # (alias: ti) class overview (fields, methods)
|
|
41
|
+
jdt source <FQN> [method] [--arity N] # (alias: src) source code (project + libraries)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Testing & building
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
jdt build [--project <name>] [--clean] # (alias: b) build project
|
|
48
|
+
jdt test <FQN> [method] [--timeout N] # run JUnit test class or method
|
|
49
|
+
jdt test --project <name> [--package <pkg>] # run tests in project/package
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
All commands auto-refresh from disk. `build` is the only command that triggers explicit builds.
|
|
53
|
+
|
|
54
|
+
### Diagnostics
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
jdt errors [--project <name>] [--file <path>] # (alias: err) compilation errors
|
|
58
|
+
jdt errors --warnings --all # include warnings and all marker types
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
File paths are workspace-relative: `my-app/src/main/java/.../Foo.java`.
|
|
62
|
+
|
|
63
|
+
### Refactoring
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
jdt organize-imports <file> # (alias: oi) organize imports
|
|
67
|
+
jdt format <file> # (alias: fmt) format code (Eclipse settings)
|
|
68
|
+
jdt rename <FQN> <newName> # rename type
|
|
69
|
+
jdt rename <FQN> <newName> --method <old> # rename method
|
|
70
|
+
jdt rename <FQN> <newName> --field <old> # rename field
|
|
71
|
+
jdt move <FQN> <target.package> # move type to another package
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Editor
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
jdt active-editor # (alias: ae) current file and cursor line
|
|
78
|
+
jdt open <FQN> [method] [--arity N] # open in Eclipse editor
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Instance discovery
|
|
82
|
+
|
|
83
|
+
The CLI reads `~/.jdtbridge/instances/*.json` to find running Eclipse instances. Each file contains port, auth token, PID, and workspace path. Stale instances are filtered by PID liveness.
|
|
84
|
+
|
|
85
|
+
When multiple instances are running, use `--workspace <hint>` or the CLI picks the first live one.
|
|
86
|
+
|
|
87
|
+
Override the home directory with `JDTBRIDGE_HOME` environment variable.
|
|
88
|
+
|
|
89
|
+
## Color output
|
|
90
|
+
|
|
91
|
+
Auto-detected from TTY. Override:
|
|
92
|
+
|
|
93
|
+
- `--color` / `--no-color` flags
|
|
94
|
+
- `FORCE_COLOR=1` / `NO_COLOR=1` env
|
|
95
|
+
- `JDTBRIDGE_COLOR=1` env
|
|
96
|
+
|
|
97
|
+
## Development
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
npm test # run tests
|
|
101
|
+
npm run test:watch # watch mode
|
|
102
|
+
```
|
package/bin/jdt.mjs
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kaluchi/jdtbridge",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "CLI for Eclipse JDT Bridge — semantic Java analysis via Eclipse JDT SearchEngine",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"jdt": "./bin/jdt.mjs",
|
|
8
|
+
"jdtbridge": "./bin/jdt.mjs"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin/",
|
|
12
|
+
"src/"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"test:watch": "vitest"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=20"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"eclipse",
|
|
23
|
+
"jdt",
|
|
24
|
+
"java",
|
|
25
|
+
"refactoring",
|
|
26
|
+
"search"
|
|
27
|
+
],
|
|
28
|
+
"license": "Apache-2.0",
|
|
29
|
+
"author": "kaluchi",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/kaluchi/jdtbridge.git",
|
|
33
|
+
"directory": "cli"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/kaluchi/jdtbridge/tree/master/cli",
|
|
36
|
+
"bugs": "https://github.com/kaluchi/jdtbridge/issues",
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
39
|
+
"vitest": "^3.0.0"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"picocolors": "^1.1.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/args.mjs
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// CLI argument parsing utilities.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Parse --flag and --key value pairs from args array.
|
|
5
|
+
* Boolean flags (standalone --flag) get value `true`.
|
|
6
|
+
* Key-value pairs (--key value) get the string value.
|
|
7
|
+
*/
|
|
8
|
+
export function parseFlags(args) {
|
|
9
|
+
const flags = {};
|
|
10
|
+
for (let i = 0; i < args.length; i++) {
|
|
11
|
+
if (args[i].startsWith("--")) {
|
|
12
|
+
const key = args[i].slice(2);
|
|
13
|
+
if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
|
|
14
|
+
flags[key] = args[++i];
|
|
15
|
+
} else {
|
|
16
|
+
flags[key] = true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return flags;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Extract positional arguments (non-flag values) from args array.
|
|
25
|
+
* Skips --flag and --key value pairs.
|
|
26
|
+
*/
|
|
27
|
+
export function extractPositional(args) {
|
|
28
|
+
const result = [];
|
|
29
|
+
for (let i = 0; i < args.length; i++) {
|
|
30
|
+
if (args[i].startsWith("--")) {
|
|
31
|
+
if (i + 1 < args.length && !args[i + 1].startsWith("--")) i++;
|
|
32
|
+
} else {
|
|
33
|
+
result.push(args[i]);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|
package/src/cli.mjs
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// Main CLI dispatcher.
|
|
2
|
+
// Maps command names to handler functions and provides help.
|
|
3
|
+
|
|
4
|
+
import { projects, help as projectsHelp } from "./commands/projects.mjs";
|
|
5
|
+
import { projectInfo, help as projectInfoHelp } from "./commands/project-info.mjs";
|
|
6
|
+
import { find, help as findHelp } from "./commands/find.mjs";
|
|
7
|
+
import { references, help as referencesHelp } from "./commands/references.mjs";
|
|
8
|
+
import { subtypes, help as subtypesHelp } from "./commands/subtypes.mjs";
|
|
9
|
+
import { hierarchy, help as hierarchyHelp } from "./commands/hierarchy.mjs";
|
|
10
|
+
import { implementors, help as implementorsHelp } from "./commands/implementors.mjs";
|
|
11
|
+
import { typeInfo, help as typeInfoHelp } from "./commands/type-info.mjs";
|
|
12
|
+
import { source, help as sourceHelp } from "./commands/source.mjs";
|
|
13
|
+
import { build, help as buildHelp } from "./commands/build.mjs";
|
|
14
|
+
import { test, help as testHelp } from "./commands/test.mjs";
|
|
15
|
+
import { errors, help as errorsHelp } from "./commands/errors.mjs";
|
|
16
|
+
import {
|
|
17
|
+
organizeImports,
|
|
18
|
+
format,
|
|
19
|
+
rename,
|
|
20
|
+
move,
|
|
21
|
+
organizeImportsHelp,
|
|
22
|
+
formatHelp,
|
|
23
|
+
renameHelp,
|
|
24
|
+
moveHelp,
|
|
25
|
+
} from "./commands/refactoring.mjs";
|
|
26
|
+
import {
|
|
27
|
+
activeEditor,
|
|
28
|
+
open,
|
|
29
|
+
activeEditorHelp,
|
|
30
|
+
openHelp,
|
|
31
|
+
} from "./commands/editor.mjs";
|
|
32
|
+
import { setup, help as setupHelp } from "./commands/setup.mjs";
|
|
33
|
+
import { isConnectionError } from "./client.mjs";
|
|
34
|
+
import { bold, red, dim } from "./color.mjs";
|
|
35
|
+
|
|
36
|
+
const commands = {
|
|
37
|
+
projects: { fn: projects, help: projectsHelp },
|
|
38
|
+
"project-info": { fn: projectInfo, help: projectInfoHelp },
|
|
39
|
+
find: { fn: find, help: findHelp },
|
|
40
|
+
references: { fn: references, help: referencesHelp },
|
|
41
|
+
subtypes: { fn: subtypes, help: subtypesHelp },
|
|
42
|
+
hierarchy: { fn: hierarchy, help: hierarchyHelp },
|
|
43
|
+
implementors: { fn: implementors, help: implementorsHelp },
|
|
44
|
+
"type-info": { fn: typeInfo, help: typeInfoHelp },
|
|
45
|
+
source: { fn: source, help: sourceHelp },
|
|
46
|
+
build: { fn: build, help: buildHelp },
|
|
47
|
+
test: { fn: test, help: testHelp },
|
|
48
|
+
errors: { fn: errors, help: errorsHelp },
|
|
49
|
+
"organize-imports": { fn: organizeImports, help: organizeImportsHelp },
|
|
50
|
+
format: { fn: format, help: formatHelp },
|
|
51
|
+
rename: { fn: rename, help: renameHelp },
|
|
52
|
+
move: { fn: move, help: moveHelp },
|
|
53
|
+
"active-editor": { fn: activeEditor, help: activeEditorHelp },
|
|
54
|
+
open: { fn: open, help: openHelp },
|
|
55
|
+
setup: { fn: setup, help: setupHelp },
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** Short aliases for frequently used commands. */
|
|
59
|
+
const aliases = {
|
|
60
|
+
refs: "references",
|
|
61
|
+
impl: "implementors",
|
|
62
|
+
subt: "subtypes",
|
|
63
|
+
hier: "hierarchy",
|
|
64
|
+
pi: "project-info",
|
|
65
|
+
ti: "type-info",
|
|
66
|
+
oi: "organize-imports",
|
|
67
|
+
ae: "active-editor",
|
|
68
|
+
src: "source",
|
|
69
|
+
b: "build",
|
|
70
|
+
err: "errors",
|
|
71
|
+
fmt: "format",
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Reverse map: command name → list of its aliases (for display).
|
|
75
|
+
const aliasesOf = {};
|
|
76
|
+
for (const [short, full] of Object.entries(aliases)) {
|
|
77
|
+
(aliasesOf[full] ||= []).push(short);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Resolve a command name or alias to its full name. */
|
|
81
|
+
function resolve(name) {
|
|
82
|
+
if (commands[name]) return name;
|
|
83
|
+
return aliases[name] || null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function fmtAliases(name) {
|
|
87
|
+
const list = aliasesOf[name];
|
|
88
|
+
return list ? " " + dim("(" + list.join(", ") + ")") : "";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function printOverview() {
|
|
92
|
+
console.log(`Eclipse JDT Bridge — semantic Java analysis via Eclipse JDT SearchEngine.
|
|
93
|
+
Requires: Eclipse running with the jdtbridge plugin.
|
|
94
|
+
|
|
95
|
+
Search & navigation:
|
|
96
|
+
projects list workspace projects
|
|
97
|
+
project-info${fmtAliases("project-info")} <name> [--lines N] project overview (adaptive detail)
|
|
98
|
+
find <Name|*Pattern*> [--source-only] find type declarations
|
|
99
|
+
references${fmtAliases("references")} <FQN> [method] [--field <name>] references to type/method/field
|
|
100
|
+
subtypes${fmtAliases("subtypes")} <FQN> all subtypes/implementors
|
|
101
|
+
hierarchy${fmtAliases("hierarchy")} <FQN> full hierarchy (supers + interfaces + subtypes)
|
|
102
|
+
implementors${fmtAliases("implementors")} <FQN> <method> [--arity n] implementations of interface method
|
|
103
|
+
type-info${fmtAliases("type-info")} <FQN> class overview (fields, methods, line numbers)
|
|
104
|
+
source${fmtAliases("source")} <FQN> [method] [--arity n] type or method source code (project and libraries)
|
|
105
|
+
|
|
106
|
+
Testing & building:
|
|
107
|
+
build${fmtAliases("build")} [--project <name>] [--clean] build project (incremental or clean)
|
|
108
|
+
test <FQN> [method] run JUnit test class or method
|
|
109
|
+
test --project <name> [--package <pkg>] run tests in project/package
|
|
110
|
+
|
|
111
|
+
Diagnostics:
|
|
112
|
+
errors${fmtAliases("errors")} [--file <path>] [--project <name>] compilation errors
|
|
113
|
+
|
|
114
|
+
Refactoring:
|
|
115
|
+
organize-imports${fmtAliases("organize-imports")} <file> organize imports
|
|
116
|
+
format${fmtAliases("format")} <file> format with Eclipse project settings
|
|
117
|
+
rename <FQN> <newName> [--method|--field] rename type/method/field
|
|
118
|
+
move <FQN> <target.package> move type to another package
|
|
119
|
+
|
|
120
|
+
Editor:
|
|
121
|
+
active-editor${fmtAliases("active-editor")} current file and cursor line
|
|
122
|
+
open <FQN> [method] open in Eclipse editor
|
|
123
|
+
|
|
124
|
+
Setup:
|
|
125
|
+
setup [--check|--remove] install/check/remove Eclipse plugin
|
|
126
|
+
|
|
127
|
+
Use "jdt help <command>" for detailed usage of any command.`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function run(argv) {
|
|
131
|
+
const [command, ...rest] = argv;
|
|
132
|
+
|
|
133
|
+
if (!command || command === "--help") {
|
|
134
|
+
printOverview();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (command === "help") {
|
|
139
|
+
const topic = rest[0];
|
|
140
|
+
const resolved = topic ? resolve(topic) : null;
|
|
141
|
+
if (resolved) {
|
|
142
|
+
console.log(commands[resolved].help);
|
|
143
|
+
} else if (topic) {
|
|
144
|
+
console.error(`Unknown command: ${topic}`);
|
|
145
|
+
console.log();
|
|
146
|
+
printOverview();
|
|
147
|
+
} else {
|
|
148
|
+
printOverview();
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const resolved = resolve(command);
|
|
154
|
+
if (!resolved) {
|
|
155
|
+
console.error(`Unknown command: ${command}`);
|
|
156
|
+
console.log();
|
|
157
|
+
printOverview();
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
await commands[resolved].fn(rest);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
if (isConnectionError(e)) {
|
|
165
|
+
console.error(
|
|
166
|
+
bold(red("Eclipse JDT Bridge not responding.")) +
|
|
167
|
+
"\nCheck that Eclipse is running with the jdtbridge plugin.\n",
|
|
168
|
+
);
|
|
169
|
+
} else {
|
|
170
|
+
console.error(e.message);
|
|
171
|
+
}
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
}
|
package/src/client.mjs
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// HTTP client for JDT Bridge server.
|
|
2
|
+
|
|
3
|
+
import { request } from "node:http";
|
|
4
|
+
import { findInstance } from "./discovery.mjs";
|
|
5
|
+
import { red, bold } from "./color.mjs";
|
|
6
|
+
|
|
7
|
+
/** @type {import('./discovery.mjs').Instance|null} */
|
|
8
|
+
let _instance;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Ensure we have a connected instance. Call before any HTTP request.
|
|
12
|
+
* @param {string} [workspaceHint]
|
|
13
|
+
* @returns {import('./discovery.mjs').Instance}
|
|
14
|
+
*/
|
|
15
|
+
export function connect(workspaceHint) {
|
|
16
|
+
if (_instance) return _instance;
|
|
17
|
+
_instance = findInstance(workspaceHint);
|
|
18
|
+
if (!_instance) {
|
|
19
|
+
console.error(
|
|
20
|
+
bold(red("Eclipse JDT Bridge not running.")) +
|
|
21
|
+
"\n\nNo live instances found. Check that:" +
|
|
22
|
+
"\n 1. Eclipse is running" +
|
|
23
|
+
"\n 2. The jdtbridge plugin is installed (io.github.kaluchi.jdtbridge)" +
|
|
24
|
+
"\n 3. Instance files exist in ~/.jdtbridge/instances/",
|
|
25
|
+
);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
return _instance;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Reset cached instance (for testing). */
|
|
32
|
+
export function resetClient() {
|
|
33
|
+
_instance = null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function authHeaders() {
|
|
37
|
+
const inst = _instance;
|
|
38
|
+
return inst && inst.token
|
|
39
|
+
? { Authorization: `Bearer ${inst.token}` }
|
|
40
|
+
: {};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Parse JSON responses, tolerating non-finite numeric literals sometimes
|
|
45
|
+
* returned by the Eclipse bridge (for example `time: NaN` in test results).
|
|
46
|
+
* @param {string} data
|
|
47
|
+
* @returns {any}
|
|
48
|
+
*/
|
|
49
|
+
function parseJson(data) {
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(data);
|
|
52
|
+
} catch {
|
|
53
|
+
const sanitized = data
|
|
54
|
+
.replace(/:\s*NaN\b/g, ": null")
|
|
55
|
+
.replace(/:\s*Infinity\b/g, ": null")
|
|
56
|
+
.replace(/:\s*-Infinity\b/g, ": null");
|
|
57
|
+
if (sanitized !== data) {
|
|
58
|
+
return JSON.parse(sanitized);
|
|
59
|
+
}
|
|
60
|
+
throw new Error("Invalid JSON: " + data);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* HTTP GET request, returns parsed JSON.
|
|
66
|
+
* @param {string} path - URL path with query string
|
|
67
|
+
* @param {number} [timeoutMs=10000]
|
|
68
|
+
* @returns {Promise<any>}
|
|
69
|
+
*/
|
|
70
|
+
export function get(path, timeoutMs = 10_000) {
|
|
71
|
+
const inst = connect();
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
const req = request(
|
|
74
|
+
{
|
|
75
|
+
hostname: "127.0.0.1",
|
|
76
|
+
port: inst.port,
|
|
77
|
+
path,
|
|
78
|
+
method: "GET",
|
|
79
|
+
timeout: timeoutMs,
|
|
80
|
+
headers: authHeaders(),
|
|
81
|
+
},
|
|
82
|
+
(res) => {
|
|
83
|
+
let data = "";
|
|
84
|
+
res.on("data", (chunk) => (data += chunk));
|
|
85
|
+
res.on("end", () => {
|
|
86
|
+
if (res.statusCode !== 200) {
|
|
87
|
+
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
resolve(parseJson(data));
|
|
92
|
+
} catch (e) {
|
|
93
|
+
reject(e);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
req.on("timeout", () => {
|
|
99
|
+
req.destroy();
|
|
100
|
+
reject(new Error("Request timed out"));
|
|
101
|
+
});
|
|
102
|
+
req.on("error", reject);
|
|
103
|
+
req.end();
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* HTTP GET request, returns raw response with headers.
|
|
109
|
+
* Used for /source which returns text/plain.
|
|
110
|
+
* @param {string} path
|
|
111
|
+
* @param {number} [timeoutMs=10000]
|
|
112
|
+
* @returns {Promise<{headers: object, body: string}>}
|
|
113
|
+
*/
|
|
114
|
+
export function getRaw(path, timeoutMs = 10_000) {
|
|
115
|
+
const inst = connect();
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
const req = request(
|
|
118
|
+
{
|
|
119
|
+
hostname: "127.0.0.1",
|
|
120
|
+
port: inst.port,
|
|
121
|
+
path,
|
|
122
|
+
method: "GET",
|
|
123
|
+
timeout: timeoutMs,
|
|
124
|
+
headers: authHeaders(),
|
|
125
|
+
},
|
|
126
|
+
(res) => {
|
|
127
|
+
let data = "";
|
|
128
|
+
res.on("data", (chunk) => (data += chunk));
|
|
129
|
+
res.on("end", () => {
|
|
130
|
+
if (res.statusCode !== 200) {
|
|
131
|
+
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const contentType = res.headers["content-type"] || "";
|
|
135
|
+
if (contentType.startsWith("application/json")) {
|
|
136
|
+
try {
|
|
137
|
+
const json = parseJson(data);
|
|
138
|
+
if (json.error) {
|
|
139
|
+
reject(new Error(json.error));
|
|
140
|
+
} else {
|
|
141
|
+
resolve({ headers: res.headers, body: data });
|
|
142
|
+
}
|
|
143
|
+
} catch (e) {
|
|
144
|
+
reject(e);
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
resolve({ headers: res.headers, body: data });
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
);
|
|
152
|
+
req.on("timeout", () => {
|
|
153
|
+
req.destroy();
|
|
154
|
+
reject(new Error("Request timed out"));
|
|
155
|
+
});
|
|
156
|
+
req.on("error", reject);
|
|
157
|
+
req.end();
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Check if error is a connection refused error.
|
|
163
|
+
* @param {Error} e
|
|
164
|
+
* @returns {boolean}
|
|
165
|
+
*/
|
|
166
|
+
export function isConnectionError(e) {
|
|
167
|
+
return e.code === "ECONNREFUSED" || e.code === "ECONNRESET";
|
|
168
|
+
}
|
package/src/color.mjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Colored output with TTY auto-detection.
|
|
2
|
+
// Enable: --color flag, JDTBRIDGE_COLOR=1, or FORCE_COLOR=1
|
|
3
|
+
// Disable: --no-color flag, NO_COLOR=1
|
|
4
|
+
|
|
5
|
+
import pc from "picocolors";
|
|
6
|
+
|
|
7
|
+
let _enabled;
|
|
8
|
+
|
|
9
|
+
/** Check if color output is enabled. */
|
|
10
|
+
export function isColorEnabled() {
|
|
11
|
+
if (_enabled !== undefined) return _enabled;
|
|
12
|
+
|
|
13
|
+
if (process.env.NO_COLOR || process.argv.includes("--no-color")) {
|
|
14
|
+
_enabled = false;
|
|
15
|
+
} else if (
|
|
16
|
+
process.env.FORCE_COLOR ||
|
|
17
|
+
process.env.JDTBRIDGE_COLOR ||
|
|
18
|
+
process.argv.includes("--color")
|
|
19
|
+
) {
|
|
20
|
+
_enabled = true;
|
|
21
|
+
} else {
|
|
22
|
+
_enabled = process.stdout.isTTY === true;
|
|
23
|
+
}
|
|
24
|
+
return _enabled;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Set color enabled/disabled explicitly. */
|
|
28
|
+
export function setColorEnabled(enabled) {
|
|
29
|
+
_enabled = enabled;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function wrap(fn) {
|
|
33
|
+
return (s) => (isColorEnabled() ? fn(s) : s);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const red = wrap(pc.red);
|
|
37
|
+
export const green = wrap(pc.green);
|
|
38
|
+
export const yellow = wrap(pc.yellow);
|
|
39
|
+
export const bold = wrap(pc.bold);
|
|
40
|
+
export const dim = wrap(pc.dim);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { get } from "../client.mjs";
|
|
2
|
+
import { parseFlags } from "../args.mjs";
|
|
3
|
+
|
|
4
|
+
export async function build(args) {
|
|
5
|
+
const flags = parseFlags(args);
|
|
6
|
+
const params = [];
|
|
7
|
+
if (flags.project) params.push(`project=${encodeURIComponent(flags.project)}`);
|
|
8
|
+
if (flags.clean) params.push("clean");
|
|
9
|
+
|
|
10
|
+
let url = "/build";
|
|
11
|
+
if (params.length > 0) url += "?" + params.join("&");
|
|
12
|
+
const result = await get(url, 180_000);
|
|
13
|
+
if (result.error) {
|
|
14
|
+
console.error(result.error);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const n = result.errors || 0;
|
|
18
|
+
if (n === 0) {
|
|
19
|
+
console.log("Build complete (0 errors)");
|
|
20
|
+
} else {
|
|
21
|
+
console.log(`Build complete (${n} errors)`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const help = `Build a project via Eclipse incremental or clean builder.
|
|
27
|
+
|
|
28
|
+
Usage: jdt build [--project <name>] [--clean]
|
|
29
|
+
|
|
30
|
+
Options:
|
|
31
|
+
--project <name> project to build (omit for workspace-wide incremental)
|
|
32
|
+
--clean clean + full rebuild (requires --project)
|
|
33
|
+
|
|
34
|
+
Always refreshes from disk before building.
|
|
35
|
+
Exit code: 0 if no compilation errors, 1 if errors found.
|
|
36
|
+
|
|
37
|
+
Examples:
|
|
38
|
+
jdt build --project m8-client
|
|
39
|
+
jdt build --project m8-client --clean
|
|
40
|
+
jdt build --project m8-client --clean && jdt test --project m8-client-gwt`;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { get } from "../client.mjs";
|
|
2
|
+
import { extractPositional, parseFlags } from "../args.mjs";
|
|
3
|
+
import { stripProject } from "../paths.mjs";
|
|
4
|
+
|
|
5
|
+
export async function activeEditor() {
|
|
6
|
+
const result = await get("/active-editor");
|
|
7
|
+
if (result.error) {
|
|
8
|
+
console.error(result.error);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
if (result.file === null) {
|
|
12
|
+
console.log("(no file open)");
|
|
13
|
+
} else {
|
|
14
|
+
console.log(`${stripProject(result.file)}:${result.line}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function open(args) {
|
|
19
|
+
const pos = extractPositional(args);
|
|
20
|
+
const flags = parseFlags(args);
|
|
21
|
+
const fqn = pos[0];
|
|
22
|
+
const method = pos[1];
|
|
23
|
+
if (!fqn) {
|
|
24
|
+
console.error("Usage: open <FQN> [method] [--arity n]");
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
let url = `/open?class=${encodeURIComponent(fqn)}`;
|
|
28
|
+
if (method) url += `&method=${encodeURIComponent(method)}`;
|
|
29
|
+
if (flags.arity !== undefined && flags.arity !== true)
|
|
30
|
+
url += `&arity=${flags.arity}`;
|
|
31
|
+
const result = await get(url);
|
|
32
|
+
if (result.error) {
|
|
33
|
+
console.error(result.error);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
console.log("Opened");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const activeEditorHelp = `Show the file and cursor line of the active Eclipse editor.
|
|
40
|
+
|
|
41
|
+
Usage: jdt active-editor`;
|
|
42
|
+
|
|
43
|
+
export const openHelp = `Open a type or method in the Eclipse editor.
|
|
44
|
+
|
|
45
|
+
Usage: jdt open <FQN> [method] [--arity n]
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
jdt open app.m8.dao.StaffDaoImpl
|
|
49
|
+
jdt open app.m8.dao.StaffDaoImpl getStaff`;
|