@unextension/cli 0.0.0 → 0.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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/__tests__/config.test.d.ts +2 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +39 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/shared.test.d.ts +2 -0
- package/dist/__tests__/shared.test.d.ts.map +1 -0
- package/dist/__tests__/shared.test.js +42 -0
- package/dist/__tests__/shared.test.js.map +1 -0
- package/dist/build.d.ts +2 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +47 -0
- package/dist/build.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +35 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/build.d.ts +2 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +60 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/dev.d.ts +2 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +113 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +94 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +16 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/config.d.ts +100 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +4 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +2 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +52 -0
- package/dist/init.js.map +1 -0
- package/dist/loader.d.ts +3 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +28 -0
- package/dist/loader.js.map +1 -0
- package/dist/targets/jetbrains/actions/ListProjectFiles.kt +19 -0
- package/dist/targets/jetbrains/actions/Notify.kt +18 -0
- package/dist/targets/jetbrains/actions/ReadProjectFile.kt +12 -0
- package/dist/targets/jetbrains/actions/RunCommand.kt +25 -0
- package/dist/targets/jetbrains/actions/RunScript.kt +57 -0
- package/dist/targets/jetbrains/actions/WriteProjectFile.kt +16 -0
- package/dist/targets/jetbrains/actions.d.ts +8 -0
- package/dist/targets/jetbrains/actions.d.ts.map +1 -0
- package/dist/targets/jetbrains/actions.js +39 -0
- package/dist/targets/jetbrains/actions.js.map +1 -0
- package/dist/targets/jetbrains/index.d.ts +3 -0
- package/dist/targets/jetbrains/index.d.ts.map +1 -0
- package/dist/targets/jetbrains/index.js +202 -0
- package/dist/targets/jetbrains/index.js.map +1 -0
- package/dist/targets/jetbrains/toolwindow.d.ts +2 -0
- package/dist/targets/jetbrains/toolwindow.d.ts.map +1 -0
- package/dist/targets/jetbrains/toolwindow.js +162 -0
- package/dist/targets/jetbrains/toolwindow.js.map +1 -0
- package/dist/targets/shared.d.ts +5 -0
- package/dist/targets/shared.d.ts.map +1 -0
- package/dist/targets/shared.js +23 -0
- package/dist/targets/shared.js.map +1 -0
- package/dist/targets/vscode/actions/globals.js +23 -0
- package/dist/targets/vscode/actions/jsconfig.json +11 -0
- package/dist/targets/vscode/actions/list-project-files.js +27 -0
- package/dist/targets/vscode/actions/notify.js +17 -0
- package/dist/targets/vscode/actions/read-project-file.js +24 -0
- package/dist/targets/vscode/actions/run-command.js +42 -0
- package/dist/targets/vscode/actions/run-script.js +39 -0
- package/dist/targets/vscode/actions/write-project-file.js +24 -0
- package/dist/targets/vscode/actions.d.ts +2 -0
- package/dist/targets/vscode/actions.d.ts.map +1 -0
- package/dist/targets/vscode/actions.js +40 -0
- package/dist/targets/vscode/actions.js.map +1 -0
- package/dist/targets/vscode/extension.d.ts +3 -0
- package/dist/targets/vscode/extension.d.ts.map +1 -0
- package/dist/targets/vscode/extension.js +107 -0
- package/dist/targets/vscode/extension.js.map +1 -0
- package/dist/targets/vscode/index.d.ts +3 -0
- package/dist/targets/vscode/index.d.ts.map +1 -0
- package/dist/targets/vscode/index.js +99 -0
- package/dist/targets/vscode/index.js.map +1 -0
- package/package.json +44 -2
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export interface JetBrainsConfig {
|
|
2
|
+
/** Open Chrome DevTools automatically (set internally by `unextension dev`) */
|
|
3
|
+
_devMode?: boolean;
|
|
4
|
+
/** Download the Gradle wrapper jar during sync (default: true) */
|
|
5
|
+
downloadGradleWrapper?: boolean;
|
|
6
|
+
/** IntelliJ IDEA Community version to build against (default: '2024.3') */
|
|
7
|
+
ideVersion?: string;
|
|
8
|
+
/** Gradle version for the wrapper (default: '8.7') */
|
|
9
|
+
gradleVersion?: string;
|
|
10
|
+
/** org.jetbrains.kotlin.jvm plugin version (default: '2.1.0') */
|
|
11
|
+
kotlinVersion?: string;
|
|
12
|
+
/** org.jetbrains.intellij.platform plugin version (default: '2.2.1') */
|
|
13
|
+
intellijPlatformVersion?: string;
|
|
14
|
+
/** JVM toolchain version (default: 21) */
|
|
15
|
+
jvmTarget?: number;
|
|
16
|
+
/** Gradle group id (default: 'com.unextension') */
|
|
17
|
+
group?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Maximum IDE build number the plugin is compatible with.
|
|
20
|
+
* Set to a specific build (e.g. '251.*') to restrict compatibility.
|
|
21
|
+
* Set to null or omit to allow all future IDE versions (default: null — no upper bound).
|
|
22
|
+
*/
|
|
23
|
+
untilBuild?: string | null;
|
|
24
|
+
}
|
|
25
|
+
export interface VSCodeConfig {
|
|
26
|
+
/** Minimum VS Code engine version (default: '>=1.85.0') */
|
|
27
|
+
engineVersion?: string;
|
|
28
|
+
/** @types/vscode version (default: '^1.85.0') */
|
|
29
|
+
typesVscodeVersion?: string;
|
|
30
|
+
/** @vscode/vsce version (default: '^3.0.0') */
|
|
31
|
+
vsceVersion?: string;
|
|
32
|
+
}
|
|
33
|
+
export type ViewLocation = 'sidebar' | 'panel' | 'editor';
|
|
34
|
+
export interface ViewConfig {
|
|
35
|
+
/** Unique identifier for this view (kebab-case) */
|
|
36
|
+
id: string;
|
|
37
|
+
/** Human-readable title shown in the IDE */
|
|
38
|
+
title: string;
|
|
39
|
+
/** Web route pattern this view should load, e.g. '/toolbar' or '/toolbar/*' */
|
|
40
|
+
route: string;
|
|
41
|
+
/** Where to place the view in the IDE (default: 'sidebar') */
|
|
42
|
+
location?: ViewLocation;
|
|
43
|
+
/** Path to an SVG icon file relative to the project root. If omitted a default icon is generated. */
|
|
44
|
+
icon?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface UnextensionConfig {
|
|
47
|
+
/** Extension identifier (kebab-case) */
|
|
48
|
+
name: string;
|
|
49
|
+
/** Human-readable name */
|
|
50
|
+
displayName: string;
|
|
51
|
+
/** Semver version */
|
|
52
|
+
version: string;
|
|
53
|
+
/** Publisher name (required for VS Code Marketplace publishing) */
|
|
54
|
+
publisher?: string;
|
|
55
|
+
/** Short description */
|
|
56
|
+
description?: string;
|
|
57
|
+
/** Repository URL, e.g. 'https://github.com/user/repo' */
|
|
58
|
+
repository?: string;
|
|
59
|
+
/** License identifier, e.g. 'MIT' (default: 'MIT') */
|
|
60
|
+
license?: string;
|
|
61
|
+
/** Path to an SVG icon for the plugin/extension (used as JetBrains pluginIcon, VS Code activity bar icon) */
|
|
62
|
+
icon?: string;
|
|
63
|
+
/** Path to the built web app (default: './dist') */
|
|
64
|
+
distDir?: string;
|
|
65
|
+
/**
|
|
66
|
+
* Path to a folder of Node.js scripts that can be called from the webview via `runScript(name, payload)`.
|
|
67
|
+
* Scripts receive the payload as a parsed object and must export a default async function.
|
|
68
|
+
* They are copied to the extension output and executed in a sandboxed Node.js process.
|
|
69
|
+
*/
|
|
70
|
+
scriptsDir?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Enable SPA mode. All views share a single shell HTML; the active route is set via window.__UNEXTENSION_ROUTE__.
|
|
73
|
+
* - true: uses 'index.html' as the shell
|
|
74
|
+
* - { shellPath: '_shell.html' }: uses a custom shell file relative to distDir
|
|
75
|
+
*/
|
|
76
|
+
spa?: boolean | {
|
|
77
|
+
shellPath: string;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Path to the SSR server entry relative to distDir (e.g. 'server/server.js').
|
|
81
|
+
* When set, the extension starts this server and loads views via http://localhost:serverPort.
|
|
82
|
+
* When omitted, the extension loads static files from distDir/client/index.html.
|
|
83
|
+
*/
|
|
84
|
+
serverEntry?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Port the SSR server listens on (default: 3000).
|
|
87
|
+
* Only used when serverEntry is set.
|
|
88
|
+
*/
|
|
89
|
+
serverPort?: number;
|
|
90
|
+
/** Target platforms to generate */
|
|
91
|
+
targets?: Array<'vscode' | 'jetbrains'>;
|
|
92
|
+
/** IDE views / tool windows to register */
|
|
93
|
+
views?: ViewConfig[];
|
|
94
|
+
/** VS Code-specific options */
|
|
95
|
+
vscode?: VSCodeConfig;
|
|
96
|
+
/** JetBrains-specific options */
|
|
97
|
+
jetbrains?: JetBrainsConfig;
|
|
98
|
+
}
|
|
99
|
+
export declare function defineConfig(config: UnextensionConfig): UnextensionConfig;
|
|
100
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,kEAAkE;IAClE,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,sDAAsD;IACtD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,wEAAwE;IACxE,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,iDAAiD;IACjD,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,MAAM,YAAY,GACpB,SAAS,GACT,OAAO,GACP,QAAQ,CAAA;AAEZ,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAA;IACV,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAA;IACb,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,qGAAqG;IACrG,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAA;IACZ,0BAA0B;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,wBAAwB;IACxB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6GAA6G;IAC7G,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,GAAG,CAAC,EAAE,OAAO,GAAG;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IACrC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,mCAAmC;IACnC,OAAO,CAAC,EAAE,KAAK,CAAC,QAAQ,GAAG,WAAW,CAAC,CAAA;IACvC,2CAA2C;IAC3C,KAAK,CAAC,EAAE,UAAU,EAAE,CAAA;IACpB,+BAA+B;IAC/B,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,iCAAiC;IACjC,SAAS,CAAC,EAAE,eAAe,CAAA;CAC5B;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,CAEzE"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAwGA,MAAM,UAAU,YAAY,CAAC,MAAyB;IACpD,OAAO,MAAM,CAAA;AACf,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/init.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAGA,wBAAsB,IAAI,CAAC,GAAG,EAAE,MAAM,iBAmDrC"}
|
package/dist/init.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
export async function init(cwd) {
|
|
4
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
5
|
+
const hasTsConfig = await fs.pathExists(path.join(cwd, 'tsconfig.json'));
|
|
6
|
+
// Read name/version/description from package.json if available
|
|
7
|
+
let name = path.basename(cwd);
|
|
8
|
+
let version = '0.0.1';
|
|
9
|
+
let description = '';
|
|
10
|
+
if (await fs.pathExists(pkgPath)) {
|
|
11
|
+
const pkg = await fs.readJson(pkgPath);
|
|
12
|
+
if (pkg.name)
|
|
13
|
+
name = pkg.name.replace(/^@[^/]+\//, ''); // strip scope
|
|
14
|
+
if (pkg.version)
|
|
15
|
+
version = pkg.version;
|
|
16
|
+
if (pkg.description)
|
|
17
|
+
description = pkg.description;
|
|
18
|
+
}
|
|
19
|
+
const configFile = hasTsConfig ? 'unextension.config.ts' : 'unextension.config.js';
|
|
20
|
+
const configPath = path.join(cwd, configFile);
|
|
21
|
+
if (await fs.pathExists(configPath)) {
|
|
22
|
+
console.log(`\n⚠️ ${configFile} already exists, skipping.\n`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const configContent = hasTsConfig
|
|
26
|
+
? `import { defineConfig } from 'unextension'\n\nexport default defineConfig({\n name: '${name}',\n displayName: '${toDisplayName(name)}',\n version: '${version}',\n description: '${description}',\n distDir: './dist',\n targets: ['vscode', 'jetbrains'],\n})\n`
|
|
27
|
+
: `import { defineConfig } from 'unextension'\n\nexport default defineConfig({\n name: '${name}',\n displayName: '${toDisplayName(name)}',\n version: '${version}',\n description: '${description}',\n distDir: './dist',\n targets: ['vscode', 'jetbrains'],\n})\n`;
|
|
28
|
+
await fs.writeFile(configPath, configContent);
|
|
29
|
+
console.log(`\n⚡ unextension init\n`);
|
|
30
|
+
console.log(` ✓ Created ${configFile}`);
|
|
31
|
+
// Add @types/vscode to devDependencies if not present
|
|
32
|
+
if (await fs.pathExists(pkgPath)) {
|
|
33
|
+
const pkg = await fs.readJson(pkgPath);
|
|
34
|
+
const devDeps = pkg.devDependencies || {};
|
|
35
|
+
let changed = false;
|
|
36
|
+
if (!devDeps['@types/vscode']) {
|
|
37
|
+
devDeps['@types/vscode'] = '^1.85.0';
|
|
38
|
+
changed = true;
|
|
39
|
+
}
|
|
40
|
+
if (changed) {
|
|
41
|
+
pkg.devDependencies = devDeps;
|
|
42
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
43
|
+
console.log(` ✓ Added @types/vscode to devDependencies in package.json`);
|
|
44
|
+
console.log(`\n Run your package manager install to install new dependencies.\n`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
console.log();
|
|
48
|
+
}
|
|
49
|
+
function toDisplayName(name) {
|
|
50
|
+
return name.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
package/dist/loader.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAIpD,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA2BxE"}
|
package/dist/loader.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { cosmiconfig } from 'cosmiconfig';
|
|
2
|
+
import { TypeScriptLoader } from 'cosmiconfig-typescript-loader';
|
|
3
|
+
const MODULE_NAME = 'unextension';
|
|
4
|
+
export async function loadConfig(cwd) {
|
|
5
|
+
const explorer = cosmiconfig(MODULE_NAME, {
|
|
6
|
+
searchPlaces: [
|
|
7
|
+
`${MODULE_NAME}.config.ts`,
|
|
8
|
+
`${MODULE_NAME}.config.js`,
|
|
9
|
+
`${MODULE_NAME}.config.mjs`,
|
|
10
|
+
`${MODULE_NAME}.config.json`,
|
|
11
|
+
`${MODULE_NAME}.config.yaml`,
|
|
12
|
+
`${MODULE_NAME}.config.yml`,
|
|
13
|
+
`.${MODULE_NAME}rc`,
|
|
14
|
+
`.${MODULE_NAME}rc.json`,
|
|
15
|
+
`.${MODULE_NAME}rc.yaml`,
|
|
16
|
+
'package.json',
|
|
17
|
+
],
|
|
18
|
+
loaders: {
|
|
19
|
+
'.ts': TypeScriptLoader(),
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
const result = await explorer.search(cwd);
|
|
23
|
+
if (!result || result.isEmpty) {
|
|
24
|
+
throw new Error(`No unextension config found. Create an unextension.config.ts in your project root.`);
|
|
25
|
+
}
|
|
26
|
+
return result.config;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAGhE,MAAM,WAAW,GAAG,aAAa,CAAA;AAEjC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW;IAC1C,MAAM,QAAQ,GAAG,WAAW,CAAC,WAAW,EAAE;QACxC,YAAY,EAAE;YACZ,GAAG,WAAW,YAAY;YAC1B,GAAG,WAAW,YAAY;YAC1B,GAAG,WAAW,aAAa;YAC3B,GAAG,WAAW,cAAc;YAC5B,GAAG,WAAW,cAAc;YAC5B,GAAG,WAAW,aAAa;YAC3B,IAAI,WAAW,IAAI;YACnB,IAAI,WAAW,SAAS;YACxB,IAAI,WAAW,SAAS;YACxB,cAAc;SACf;QACD,OAAO,EAAE;YACP,KAAK,EAAE,gBAAgB,EAAE;SAC1B;KACF,CAAC,CAAA;IAEF,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACzC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,oFAAoF,CACrF,CAAA;IACH,CAAC;IAED,OAAO,MAAM,CAAC,MAA2B,CAAA;AAC3C,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
fun handleListProjectFiles(payload: org.json.JSONObject?, reply: org.json.JSONObject) {
|
|
2
|
+
val projectPath = com.intellij.openapi.project.ProjectManager.getInstance().openProjects.firstOrNull()?.basePath ?: ""
|
|
3
|
+
val pattern = payload?.optString("pattern", "**/*") ?: "**/*"
|
|
4
|
+
val extFilter = Regex("""\*\*\/\*\.([a-zA-Z0-9]+)$""").find(pattern)?.groupValues?.get(1)
|
|
5
|
+
val files = if (projectPath.isNotEmpty()) {
|
|
6
|
+
val root = java.io.File(projectPath)
|
|
7
|
+
root.walkTopDown()
|
|
8
|
+
.onEnter { dir ->
|
|
9
|
+
val name = dir.name
|
|
10
|
+
name != "node_modules" && name != ".git" && name != ".idea" &&
|
|
11
|
+
name != ".gradle" && name != "build" && name != "out"
|
|
12
|
+
}
|
|
13
|
+
.filter { it.isFile && (extFilter == null || it.extension == extFilter) }
|
|
14
|
+
.map { it.relativeTo(root).path.replace("\\", "/") }
|
|
15
|
+
.toList()
|
|
16
|
+
} else emptyList()
|
|
17
|
+
reply.put("type", "list-project-files:reply")
|
|
18
|
+
reply.put("payload", org.json.JSONArray(files))
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
fun handleNotify(payload: org.json.JSONObject?, reply: org.json.JSONObject, project: Project) {
|
|
2
|
+
if (payload == null) return
|
|
3
|
+
val message = payload.optString("message", "")
|
|
4
|
+
val level = payload.optString("level", "info")
|
|
5
|
+
val notificationType = when (level) {
|
|
6
|
+
"error" -> com.intellij.notification.NotificationType.ERROR
|
|
7
|
+
"warning" -> com.intellij.notification.NotificationType.WARNING
|
|
8
|
+
else -> com.intellij.notification.NotificationType.INFORMATION
|
|
9
|
+
}
|
|
10
|
+
com.intellij.notification.NotificationGroupManager.getInstance()
|
|
11
|
+
.getNotificationGroup("unextension")
|
|
12
|
+
?.createNotification(message, notificationType)
|
|
13
|
+
?.notify(project)
|
|
14
|
+
reply.put("type", "notify:reply")
|
|
15
|
+
val replyPayload = org.json.JSONObject()
|
|
16
|
+
replyPayload.put("shown", true)
|
|
17
|
+
reply.put("payload", replyPayload)
|
|
18
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
fun handleReadProjectFile(payload: org.json.JSONObject?, reply: org.json.JSONObject) {
|
|
2
|
+
if (payload == null) return
|
|
3
|
+
val filePath = payload.optString("path", "")
|
|
4
|
+
val projectPath = com.intellij.openapi.project.ProjectManager.getInstance().openProjects.firstOrNull()?.basePath ?: ""
|
|
5
|
+
val file = java.io.File(projectPath, filePath)
|
|
6
|
+
val content = if (file.exists() && file.isFile) file.readText(Charsets.UTF_8) else ""
|
|
7
|
+
reply.put("type", "read-project-file:reply")
|
|
8
|
+
val replyPayload = org.json.JSONObject()
|
|
9
|
+
replyPayload.put("content", content)
|
|
10
|
+
replyPayload.put("encoding", "utf8")
|
|
11
|
+
reply.put("payload", replyPayload)
|
|
12
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
fun handleRunCommand(payload: org.json.JSONObject?, reply: org.json.JSONObject) {
|
|
2
|
+
if (payload == null) return
|
|
3
|
+
val command = payload.optString("command", "")
|
|
4
|
+
val requestedShell = payload.optString("shell", "")
|
|
5
|
+
val isWindows = System.getProperty("os.name").lowercase().contains("win")
|
|
6
|
+
val (shell, flag) = resolveShell(requestedShell, isWindows)
|
|
7
|
+
val result = runShellCommand(command, shell, flag)
|
|
8
|
+
reply.put("type", "run-command:reply")
|
|
9
|
+
val replyPayload = org.json.JSONObject()
|
|
10
|
+
replyPayload.put("command", command)
|
|
11
|
+
replyPayload.put("stdout", result.first)
|
|
12
|
+
replyPayload.put("stderr", result.second)
|
|
13
|
+
replyPayload.put("exitCode", result.third)
|
|
14
|
+
reply.put("payload", replyPayload)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fun resolveShell(requested: String, isWindows: Boolean): Pair<String, String> = when (requested) {
|
|
18
|
+
"cmd" -> Pair("cmd", "/c")
|
|
19
|
+
"powershell" -> Pair("powershell", "-Command")
|
|
20
|
+
"bash" -> Pair("bash", "-c")
|
|
21
|
+
"sh" -> Pair("sh", "-c")
|
|
22
|
+
"zsh" -> Pair("zsh", "-c")
|
|
23
|
+
"fish" -> Pair("fish", "-c")
|
|
24
|
+
else -> if (isWindows) Pair("cmd", "/c") else Pair("sh", "-c")
|
|
25
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
fun handleRunScript(payload: org.json.JSONObject?, reply: org.json.JSONObject) {
|
|
2
|
+
if (payload == null) return
|
|
3
|
+
val name = payload.optString("name", "")
|
|
4
|
+
val scriptPayload = payload.opt("payload")
|
|
5
|
+
val scriptName = if (name.endsWith(".js")) name else "$name.js"
|
|
6
|
+
|
|
7
|
+
// Extract script from classpath resources to a temp dir and run it
|
|
8
|
+
val tmpDir = java.io.File(System.getProperty("java.io.tmpdir"), "unextension-scripts")
|
|
9
|
+
tmpDir.mkdirs()
|
|
10
|
+
val outFile = java.io.File(tmpDir, scriptName)
|
|
11
|
+
val stream = object {}.javaClass.getResourceAsStream("/scripts/$scriptName")
|
|
12
|
+
|
|
13
|
+
if (stream == null) {
|
|
14
|
+
reply.put("type", "run-script:reply")
|
|
15
|
+
val replyPayload = org.json.JSONObject()
|
|
16
|
+
replyPayload.put("result", org.json.JSONObject.NULL)
|
|
17
|
+
replyPayload.put("exitCode", 1)
|
|
18
|
+
replyPayload.put("stderr", "Script not found: $scriptName")
|
|
19
|
+
reply.put("payload", replyPayload)
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
stream.use { it.copyTo(outFile.outputStream()) }
|
|
23
|
+
|
|
24
|
+
val inputJson = scriptPayload?.toString() ?: "null"
|
|
25
|
+
val nodePath = resolveNode()
|
|
26
|
+
val proc = ProcessBuilder(nodePath, outFile.absolutePath)
|
|
27
|
+
.apply { environment()["UNEXTENSION_PAYLOAD"] = inputJson }
|
|
28
|
+
.redirectErrorStream(false)
|
|
29
|
+
.start()
|
|
30
|
+
val stdout = proc.inputStream.bufferedReader().readText()
|
|
31
|
+
val stderr = proc.errorStream.bufferedReader().readText()
|
|
32
|
+
val exitCode = proc.waitFor()
|
|
33
|
+
|
|
34
|
+
var result: Any = org.json.JSONObject.NULL
|
|
35
|
+
try { result = org.json.JSONObject(stdout.trim()) } catch (_: Exception) {
|
|
36
|
+
try { result = org.json.JSONArray(stdout.trim()) } catch (_: Exception) {
|
|
37
|
+
result = stdout.trim()
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
reply.put("type", "run-script:reply")
|
|
42
|
+
val replyPayload = org.json.JSONObject()
|
|
43
|
+
replyPayload.put("result", result as Any)
|
|
44
|
+
replyPayload.put("exitCode", exitCode)
|
|
45
|
+
replyPayload.put("stderr", stderr)
|
|
46
|
+
reply.put("payload", replyPayload)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fun resolveNode(): String {
|
|
50
|
+
val candidates = if (System.getProperty("os.name").lowercase().contains("win"))
|
|
51
|
+
listOf("node.exe", "C:\\Program Files\\nodejs\\node.exe")
|
|
52
|
+
else
|
|
53
|
+
listOf("node", "/usr/local/bin/node", "/usr/bin/node")
|
|
54
|
+
return candidates.firstOrNull { java.io.File(it).exists() || runCatching {
|
|
55
|
+
ProcessBuilder("which", it).start().waitFor() == 0
|
|
56
|
+
}.getOrDefault(false) } ?: "node"
|
|
57
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
fun handleWriteProjectFile(payload: org.json.JSONObject?, reply: org.json.JSONObject) {
|
|
2
|
+
if (payload == null) return
|
|
3
|
+
val filePath = payload.optString("path", "")
|
|
4
|
+
val content = payload.optString("content", "")
|
|
5
|
+
val projectPath = com.intellij.openapi.project.ProjectManager.getInstance().openProjects.firstOrNull()?.basePath ?: ""
|
|
6
|
+
val file = java.io.File(projectPath, filePath)
|
|
7
|
+
val success = try {
|
|
8
|
+
file.parentFile?.mkdirs()
|
|
9
|
+
file.writeText(content, Charsets.UTF_8)
|
|
10
|
+
true
|
|
11
|
+
} catch (e: Exception) { false }
|
|
12
|
+
reply.put("type", "write-project-file:reply")
|
|
13
|
+
val replyPayload = org.json.JSONObject()
|
|
14
|
+
replyPayload.put("success", success)
|
|
15
|
+
reply.put("payload", replyPayload)
|
|
16
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface KotlinActions {
|
|
2
|
+
/** Private fun declarations to embed as class members */
|
|
3
|
+
functions: string;
|
|
4
|
+
/** if/else if dispatch chain to embed in the message handler */
|
|
5
|
+
dispatch: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function generateKotlinActions(): KotlinActions;
|
|
8
|
+
//# sourceMappingURL=actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../../src/targets/jetbrains/actions.ts"],"names":[],"mappings":"AA+BA,MAAM,WAAW,aAAa;IAC5B,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAA;IACjB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,qBAAqB,IAAI,aAAa,CAuBrD"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join, dirname, basename } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
const actionsDir = join(dirname(fileURLToPath(import.meta.url)), 'actions');
|
|
5
|
+
function pascalToKebab(s) {
|
|
6
|
+
return s.replace(/([A-Z])/g, (_, c, i) => (i > 0 ? '-' : '') + c.toLowerCase());
|
|
7
|
+
}
|
|
8
|
+
function loadActions() {
|
|
9
|
+
return readdirSync(actionsDir)
|
|
10
|
+
.filter((f) => f.endsWith('.kt'))
|
|
11
|
+
.sort()
|
|
12
|
+
.map((f) => {
|
|
13
|
+
const name = basename(f, '.kt');
|
|
14
|
+
const fnName = `handle${name}`;
|
|
15
|
+
const type = pascalToKebab(name);
|
|
16
|
+
const body = readFileSync(join(actionsDir, f), 'utf8').trim();
|
|
17
|
+
const needsProject = body.includes(', project: Project');
|
|
18
|
+
return { type, fnName, body, needsProject };
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export function generateKotlinActions() {
|
|
22
|
+
const actions = loadActions();
|
|
23
|
+
const functions = actions
|
|
24
|
+
.map((a) => a.body
|
|
25
|
+
.split('\n')
|
|
26
|
+
.map((l) => ' ' + l)
|
|
27
|
+
.join('\n'))
|
|
28
|
+
.join('\n\n');
|
|
29
|
+
const dispatch = actions
|
|
30
|
+
.map((a, i) => {
|
|
31
|
+
const call = `${a.fnName}(payload, reply${a.needsProject ? ', project' : ''})`;
|
|
32
|
+
const cond = `type == "${a.type}"`;
|
|
33
|
+
return i === 0 ? `if (${cond}) { ${call} }` : `else if (${cond}) { ${call} }`;
|
|
34
|
+
})
|
|
35
|
+
.join('\n ') +
|
|
36
|
+
`\n else {\n reply.put("type", "$type:reply")\n val replyPayload = org.json.JSONObject()\n replyPayload.put("received", true)\n replyPayload.put("echo", parsed.opt("payload"))\n reply.put("payload", replyPayload)\n }`;
|
|
37
|
+
return { functions, dispatch };
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.js","sourceRoot":"","sources":["../../../src/targets/jetbrains/actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACnD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;AAS3E,SAAS,aAAa,CAAC,CAAS;IAC9B,OAAO,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAA;AACjF,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,WAAW,CAAC,UAAU,CAAC;SAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;SAChC,IAAI,EAAE;SACN,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;QAC/B,MAAM,MAAM,GAAG,SAAS,IAAI,EAAE,CAAA;QAC9B,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAA;QAChC,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAA;QAC7D,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAA;QACxD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,CAAA;IAC7C,CAAC,CAAC,CAAA;AACN,CAAC;AASD,MAAM,UAAU,qBAAqB;IACnC,MAAM,OAAO,GAAG,WAAW,EAAE,CAAA;IAE7B,MAAM,SAAS,GAAG,OAAO;SACtB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACT,CAAC,CAAC,IAAI;SACH,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;SACtB,IAAI,CAAC,IAAI,CAAC,CACd;SACA,IAAI,CAAC,MAAM,CAAC,CAAA;IAEf,MAAM,QAAQ,GACZ,OAAO;SACJ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACZ,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,MAAM,kBAAkB,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,GAAG,CAAA;QAC9E,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,IAAI,GAAG,CAAA;QAClC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,YAAY,IAAI,OAAO,IAAI,IAAI,CAAA;IAC/E,CAAC,CAAC;SACD,IAAI,CAAC,oBAAoB,CAAC;QAC7B,sVAAsV,CAAA;IAExV,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAA;AAChC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/targets/jetbrains/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAaxD,wBAAsB,cAAc,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,iBA+I1E"}
|