@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.
Files changed (94) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +15 -0
  4. package/dist/__tests__/config.test.d.ts +2 -0
  5. package/dist/__tests__/config.test.d.ts.map +1 -0
  6. package/dist/__tests__/config.test.js +39 -0
  7. package/dist/__tests__/config.test.js.map +1 -0
  8. package/dist/__tests__/shared.test.d.ts +2 -0
  9. package/dist/__tests__/shared.test.d.ts.map +1 -0
  10. package/dist/__tests__/shared.test.js +42 -0
  11. package/dist/__tests__/shared.test.js.map +1 -0
  12. package/dist/build.d.ts +2 -0
  13. package/dist/build.d.ts.map +1 -0
  14. package/dist/build.js +47 -0
  15. package/dist/build.js.map +1 -0
  16. package/dist/cli.d.ts +3 -0
  17. package/dist/cli.d.ts.map +1 -0
  18. package/dist/cli.js +35 -0
  19. package/dist/cli.js.map +1 -0
  20. package/dist/commands/build.d.ts +2 -0
  21. package/dist/commands/build.d.ts.map +1 -0
  22. package/dist/commands/build.js +60 -0
  23. package/dist/commands/build.js.map +1 -0
  24. package/dist/commands/dev.d.ts +2 -0
  25. package/dist/commands/dev.d.ts.map +1 -0
  26. package/dist/commands/dev.js +113 -0
  27. package/dist/commands/dev.js.map +1 -0
  28. package/dist/commands/init.d.ts +2 -0
  29. package/dist/commands/init.d.ts.map +1 -0
  30. package/dist/commands/init.js +94 -0
  31. package/dist/commands/init.js.map +1 -0
  32. package/dist/commands/sync.d.ts +2 -0
  33. package/dist/commands/sync.d.ts.map +1 -0
  34. package/dist/commands/sync.js +16 -0
  35. package/dist/commands/sync.js.map +1 -0
  36. package/dist/config.d.ts +100 -0
  37. package/dist/config.d.ts.map +1 -0
  38. package/dist/config.js +4 -0
  39. package/dist/config.js.map +1 -0
  40. package/dist/index.d.ts +3 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +2 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/init.d.ts +2 -0
  45. package/dist/init.d.ts.map +1 -0
  46. package/dist/init.js +52 -0
  47. package/dist/init.js.map +1 -0
  48. package/dist/loader.d.ts +3 -0
  49. package/dist/loader.d.ts.map +1 -0
  50. package/dist/loader.js +28 -0
  51. package/dist/loader.js.map +1 -0
  52. package/dist/targets/jetbrains/actions/ListProjectFiles.kt +19 -0
  53. package/dist/targets/jetbrains/actions/Notify.kt +18 -0
  54. package/dist/targets/jetbrains/actions/ReadProjectFile.kt +12 -0
  55. package/dist/targets/jetbrains/actions/RunCommand.kt +25 -0
  56. package/dist/targets/jetbrains/actions/RunScript.kt +57 -0
  57. package/dist/targets/jetbrains/actions/WriteProjectFile.kt +16 -0
  58. package/dist/targets/jetbrains/actions.d.ts +8 -0
  59. package/dist/targets/jetbrains/actions.d.ts.map +1 -0
  60. package/dist/targets/jetbrains/actions.js +39 -0
  61. package/dist/targets/jetbrains/actions.js.map +1 -0
  62. package/dist/targets/jetbrains/index.d.ts +3 -0
  63. package/dist/targets/jetbrains/index.d.ts.map +1 -0
  64. package/dist/targets/jetbrains/index.js +202 -0
  65. package/dist/targets/jetbrains/index.js.map +1 -0
  66. package/dist/targets/jetbrains/toolwindow.d.ts +2 -0
  67. package/dist/targets/jetbrains/toolwindow.d.ts.map +1 -0
  68. package/dist/targets/jetbrains/toolwindow.js +162 -0
  69. package/dist/targets/jetbrains/toolwindow.js.map +1 -0
  70. package/dist/targets/shared.d.ts +5 -0
  71. package/dist/targets/shared.d.ts.map +1 -0
  72. package/dist/targets/shared.js +23 -0
  73. package/dist/targets/shared.js.map +1 -0
  74. package/dist/targets/vscode/actions/globals.js +23 -0
  75. package/dist/targets/vscode/actions/jsconfig.json +11 -0
  76. package/dist/targets/vscode/actions/list-project-files.js +27 -0
  77. package/dist/targets/vscode/actions/notify.js +17 -0
  78. package/dist/targets/vscode/actions/read-project-file.js +24 -0
  79. package/dist/targets/vscode/actions/run-command.js +42 -0
  80. package/dist/targets/vscode/actions/run-script.js +39 -0
  81. package/dist/targets/vscode/actions/write-project-file.js +24 -0
  82. package/dist/targets/vscode/actions.d.ts +2 -0
  83. package/dist/targets/vscode/actions.d.ts.map +1 -0
  84. package/dist/targets/vscode/actions.js +40 -0
  85. package/dist/targets/vscode/actions.js.map +1 -0
  86. package/dist/targets/vscode/extension.d.ts +3 -0
  87. package/dist/targets/vscode/extension.d.ts.map +1 -0
  88. package/dist/targets/vscode/extension.js +107 -0
  89. package/dist/targets/vscode/extension.js.map +1 -0
  90. package/dist/targets/vscode/index.d.ts +3 -0
  91. package/dist/targets/vscode/index.d.ts.map +1 -0
  92. package/dist/targets/vscode/index.js +99 -0
  93. package/dist/targets/vscode/index.js.map +1 -0
  94. package/package.json +44 -2
@@ -0,0 +1,202 @@
1
+ import path from 'node:path';
2
+ import https from 'node:https';
3
+ import fs from 'fs-extra';
4
+ import { toPascalCase, escapeXml, defaultPluginIconSvg } from '../shared.js';
5
+ import { generateToolWindowFactory } from './toolwindow.js';
6
+ const DEFAULT_GRADLE_VERSION = '8.7';
7
+ const DEFAULT_KOTLIN_VERSION = '2.1.0';
8
+ const DEFAULT_INTELLIJ_PLATFORM_VERSION = '2.2.1';
9
+ const DEFAULT_JVM_TARGET = 21;
10
+ const DEFAULT_IDE_VERSION = '2024.3';
11
+ const DEFAULT_GROUP = 'com.unextension';
12
+ const GRADLE_VERSION_RE = /^\d+\.\d+(\.\d+)?$/;
13
+ const ALLOWED_REDIRECT_HOSTS = ['raw.githubusercontent.com', 'objects.githubusercontent.com'];
14
+ export async function buildJetBrains(config, cwd) {
15
+ const distDir = path.resolve(cwd, config.distDir || './dist');
16
+ const outDir = path.resolve(cwd, 'output/jetbrains');
17
+ const resourcesDir = path.join(outDir, 'src/main/resources');
18
+ const jb = config.jetbrains ?? {};
19
+ const gradleVersion = jb.gradleVersion ?? DEFAULT_GRADLE_VERSION;
20
+ const kotlinVersion = jb.kotlinVersion ?? DEFAULT_KOTLIN_VERSION;
21
+ const intellijPlatformVersion = jb.intellijPlatformVersion ?? DEFAULT_INTELLIJ_PLATFORM_VERSION;
22
+ const jvmTarget = jb.jvmTarget ?? DEFAULT_JVM_TARGET;
23
+ const ideVersion = jb.ideVersion ?? DEFAULT_IDE_VERSION;
24
+ const group = jb.group ?? DEFAULT_GROUP;
25
+ const untilBuild = 'untilBuild' in jb ? jb.untilBuild : null;
26
+ const untilBuildKotlin = untilBuild
27
+ ? `untilBuild = "${untilBuild}"`
28
+ : `untilBuild = provider { null }`;
29
+ if (!(await fs.pathExists(distDir))) {
30
+ throw new Error(`distDir not found: ${distDir}\nRun your app's build first.`);
31
+ }
32
+ await fs.ensureDir(resourcesDir);
33
+ await fs.copy(distDir, path.join(resourcesDir, 'webview'));
34
+ if (config.scriptsDir) {
35
+ const scriptsDir = path.resolve(cwd, config.scriptsDir);
36
+ if (await fs.pathExists(scriptsDir)) {
37
+ await fs.copy(scriptsDir, path.join(resourcesDir, 'scripts'));
38
+ }
39
+ }
40
+ const views = config.views ?? [];
41
+ const kotlinDir = path.join(outDir, 'src/main/kotlin/com/unextension');
42
+ await fs.ensureDir(kotlinDir);
43
+ const toolWindowEntries = views.length > 0
44
+ ? views
45
+ .map((v) => {
46
+ const anchor = v.location === 'panel' ? 'bottom' : 'right';
47
+ return ` <toolWindow id="${escapeXml(v.title)}"
48
+ factoryClass="com.unextension.${toPascalCase(v.id)}ToolWindowFactory"
49
+ anchor="${anchor}"
50
+ secondary="false" />`;
51
+ })
52
+ .join('\n')
53
+ : ` <toolWindow id="${escapeXml(config.displayName)}"
54
+ factoryClass="com.unextension.${toPascalCase(config.name)}ToolWindowFactory"
55
+ anchor="right"
56
+ secondary="false" />`;
57
+ const pluginXml = `<idea-plugin>
58
+ <id>com.unextension.${escapeXml(config.name)}</id>
59
+ <name>${escapeXml(config.displayName)}</name>
60
+ <version>${escapeXml(config.version)}</version>
61
+ <description>${escapeXml(config.description || '')}</description>
62
+ <vendor>unextension</vendor>
63
+
64
+ <depends>com.intellij.modules.platform</depends>
65
+
66
+ <extensions defaultExtensionNs="com.intellij">
67
+ <notificationGroup id="unextension" displayType="BALLOON" />
68
+ ${toolWindowEntries}
69
+ </extensions>
70
+ </idea-plugin>
71
+ `;
72
+ const metaInfDir = path.join(resourcesDir, 'META-INF');
73
+ await fs.ensureDir(metaInfDir);
74
+ await fs.writeFile(path.join(metaInfDir, 'plugin.xml'), pluginXml);
75
+ const iconDestSvg = path.join(metaInfDir, 'pluginIcon.svg');
76
+ if (config.icon) {
77
+ const iconSrc = path.resolve(cwd, config.icon);
78
+ await fs.writeFile(iconDestSvg, (await fs.pathExists(iconSrc))
79
+ ? await fs.readFile(iconSrc, 'utf8')
80
+ : defaultPluginIconSvg(config.displayName));
81
+ }
82
+ else {
83
+ await fs.writeFile(iconDestSvg, defaultPluginIconSvg(config.displayName));
84
+ }
85
+ const devMode = !!config.jetbrains?._devMode;
86
+ if (views.length > 0) {
87
+ for (const view of views) {
88
+ const kt = generateToolWindowFactory(toPascalCase(view.id), view.route.replace(/\/\*$/, ''), devMode);
89
+ await fs.writeFile(path.join(kotlinDir, `${toPascalCase(view.id)}ToolWindowFactory.kt`), kt);
90
+ }
91
+ }
92
+ else {
93
+ const className = toPascalCase(config.name);
94
+ await fs.writeFile(path.join(kotlinDir, `${className}ToolWindowFactory.kt`), generateToolWindowFactory(className, '/', devMode));
95
+ }
96
+ const buildGradle = `import org.jetbrains.intellij.platform.gradle.TestFrameworkType
97
+
98
+ plugins {
99
+ id("org.jetbrains.kotlin.jvm") version "${kotlinVersion}"
100
+ id("org.jetbrains.intellij.platform") version "${intellijPlatformVersion}"
101
+ }
102
+
103
+ group = "${group}"
104
+ version = "${config.version}"
105
+
106
+ kotlin {
107
+ jvmToolchain(${jvmTarget})
108
+ }
109
+
110
+ repositories {
111
+ mavenCentral()
112
+ intellijPlatform { defaultRepositories() }
113
+ }
114
+
115
+ dependencies {
116
+ testImplementation("junit:junit:4.13.2")
117
+ implementation("org.json:json:20240303")
118
+
119
+ intellijPlatform {
120
+ intellijIdeaCommunity("${ideVersion}")
121
+ testFramework(TestFrameworkType.Platform)
122
+ }
123
+ }
124
+
125
+ intellijPlatform {
126
+ pluginConfiguration {
127
+ ideaVersion {
128
+ ${untilBuildKotlin}
129
+ }
130
+ }
131
+ }
132
+ `;
133
+ await fs.writeFile(path.join(outDir, 'build.gradle.kts'), buildGradle);
134
+ await generateGradleWrapper(outDir, jb.downloadGradleWrapper ?? true, gradleVersion);
135
+ console.log(` ✓ JetBrains plugin → output/jetbrains/`);
136
+ }
137
+ async function generateGradleWrapper(outDir, downloadJar, gradleVersion) {
138
+ const wrapperDir = path.join(outDir, 'gradle', 'wrapper');
139
+ await fs.ensureDir(wrapperDir);
140
+ await fs.writeFile(path.join(wrapperDir, 'gradle-wrapper.properties'), `distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-${gradleVersion}-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n`);
141
+ await fs.writeFile(path.join(outDir, 'gradlew'), `#!/usr/bin/env sh\nAPP_NAME="Gradle"\nAPP_BASE_NAME=$(basename "$0")\nAPP_HOME=$(cd "$(dirname "$0")" && pwd)\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\nexec java -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"\n`, { mode: 0o755 });
142
+ await fs.writeFile(path.join(outDir, 'gradlew.bat'), `@rem Gradle startup script for Windows\n@if "%DEBUG%"=="" @echo off\nset APP_HOME=%~dp0\nset CLASSPATH=%APP_HOME%gradle\\wrapper\\gradle-wrapper.jar\njava -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*\n`);
143
+ const jarPath = path.join(wrapperDir, 'gradle-wrapper.jar');
144
+ const jarValid = (await fs.pathExists(jarPath)) && (await fs.stat(jarPath)).size > 10_000;
145
+ if (!GRADLE_VERSION_RE.test(gradleVersion))
146
+ throw new Error(`Invalid gradleVersion: ${gradleVersion}`);
147
+ if (downloadJar && !jarValid) {
148
+ if (await fs.pathExists(jarPath))
149
+ await fs.remove(jarPath);
150
+ console.log(` ⬇️ Downloading gradle-wrapper.jar...`);
151
+ await downloadFile(`https://raw.githubusercontent.com/gradle/gradle/v${gradleVersion}.0/gradle/wrapper/gradle-wrapper.jar`, jarPath);
152
+ }
153
+ else if (!downloadJar) {
154
+ console.log(` ⚠️ Skipping gradle-wrapper.jar download (downloadGradleWrapper: false)`);
155
+ }
156
+ }
157
+ function downloadFile(url, dest) {
158
+ return new Promise((resolve, reject) => {
159
+ const follow = (u, redirects = 0) => {
160
+ if (redirects > 5) {
161
+ reject(new Error('Too many redirects'));
162
+ return;
163
+ }
164
+ https
165
+ .get(u, (res) => {
166
+ if ((res.statusCode === 301 || res.statusCode === 302) && res.headers.location) {
167
+ res.resume();
168
+ const next = res.headers.location;
169
+ let nextHost;
170
+ try {
171
+ nextHost = new URL(next).hostname;
172
+ }
173
+ catch {
174
+ reject(new Error(`Invalid redirect URL: ${next}`));
175
+ return;
176
+ }
177
+ if (!ALLOWED_REDIRECT_HOSTS.includes(nextHost)) {
178
+ reject(new Error(`Redirect to disallowed host: ${nextHost}`));
179
+ return;
180
+ }
181
+ follow(next, redirects + 1);
182
+ return;
183
+ }
184
+ if (res.statusCode !== 200) {
185
+ res.resume();
186
+ reject(new Error(`HTTP ${res.statusCode}`));
187
+ return;
188
+ }
189
+ const file = fs.createWriteStream(dest);
190
+ res.pipe(file);
191
+ file.on('finish', () => file.close(() => resolve()));
192
+ file.on('error', (err) => {
193
+ res.destroy();
194
+ fs.remove(dest).then(() => reject(err));
195
+ });
196
+ })
197
+ .on('error', reject);
198
+ };
199
+ follow(url);
200
+ });
201
+ }
202
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/targets/jetbrains/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function generateToolWindowFactory(className: string, route: string, devMode: boolean): string;
2
+ //# sourceMappingURL=toolwindow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolwindow.d.ts","sourceRoot":"","sources":["../../../src/targets/jetbrains/toolwindow.ts"],"names":[],"mappings":"AAEA,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,GACf,MAAM,CAkKR"}
@@ -0,0 +1,162 @@
1
+ import { generateKotlinActions } from './actions.js';
2
+ export function generateToolWindowFactory(className, route, devMode) {
3
+ const routeRelative = route === '/' || route === '' ? '' : route.replace(/^\//, '');
4
+ const classNameLower = className.toLowerCase();
5
+ const { functions: actionFunctions, dispatch: actionDispatch } = generateKotlinActions();
6
+ return `package com.unextension
7
+
8
+ import com.intellij.openapi.actionSystem.AnAction
9
+ import com.intellij.openapi.actionSystem.AnActionEvent
10
+ import com.intellij.openapi.project.Project
11
+ import com.intellij.openapi.wm.ToolWindow
12
+ import com.intellij.openapi.wm.ToolWindowFactory
13
+ import com.intellij.ui.jcef.JBCefApp
14
+ import com.intellij.ui.jcef.JBCefBrowser
15
+ import com.intellij.ui.jcef.JBCefJSQuery
16
+ import org.cef.browser.CefBrowser
17
+ import org.cef.browser.CefFrame
18
+ import org.cef.handler.CefLoadHandlerAdapter
19
+ import javax.swing.JLabel
20
+ import javax.swing.JPanel
21
+ import java.awt.BorderLayout
22
+
23
+ class ${className}ToolWindowFactory : ToolWindowFactory {
24
+ override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
25
+ val contentManager = toolWindow.contentManager
26
+
27
+ if (!JBCefApp.isSupported()) {
28
+ val fallback = JPanel(BorderLayout())
29
+ fallback.add(JLabel("JCEF is not available. Enable via Help → Find Action → Registry → ide.browser.jcef.enabled."), BorderLayout.CENTER)
30
+ contentManager.addContent(contentManager.factory.createContent(fallback, "", false))
31
+ return
32
+ }
33
+
34
+ val htmlContent = resolveWebviewHtml() ?: run {
35
+ val fallback = JPanel(BorderLayout())
36
+ fallback.add(JLabel("Webview not found. Run your app build then 'unextension sync'."), BorderLayout.CENTER)
37
+ contentManager.addContent(contentManager.factory.createContent(fallback, "", false))
38
+ return
39
+ }
40
+ val browser = JBCefBrowser(htmlContent)
41
+ val jsQuery = JBCefJSQuery.create(browser)
42
+
43
+ jsQuery.addHandler { msg ->
44
+ println("[unextension] message from webview (${classNameLower}): $msg")
45
+ try {
46
+ val parsed = org.json.JSONObject(msg)
47
+ val type = parsed.optString("type", "unknown")
48
+ val payload = parsed.optJSONObject("payload")
49
+ val correlationId = parsed.optString("correlationId", "")
50
+ val reply = org.json.JSONObject()
51
+ if (correlationId.isNotEmpty()) reply.put("correlationId", correlationId)
52
+ ${actionDispatch}
53
+
54
+ val replyJson = reply.toString().replace("\\", "\\\\").replace("'", "\\'")
55
+ val js = "window.dispatchEvent(new MessageEvent('message', { data: JSON.parse('$replyJson') }));"
56
+ browser.cefBrowser.executeJavaScript(js, browser.cefBrowser.url, 0)
57
+ } catch (e: Exception) {
58
+ println("[unextension] Failed to handle message: \${e.message}")
59
+ }
60
+ null
61
+ }
62
+
63
+ val loadHandler = object : CefLoadHandlerAdapter() {
64
+ override fun onLoadEnd(b: CefBrowser, frame: CefFrame, httpStatusCode: Int) {
65
+ if (!frame.isMain) return
66
+
67
+ fun colorHex(key: String, fallback: String): String {
68
+ val c = javax.swing.UIManager.getColor(key) ?: return fallback
69
+ return String.format("#%02x%02x%02x", c.red, c.green, c.blue)
70
+ }
71
+ val bg = colorHex("Panel.background", "#1e1e1e")
72
+ val fg = colorHex("Panel.foreground", "#d4d4d4")
73
+ val inputBg = colorHex("TextField.background", "#2d2d2d")
74
+ val inputFg = colorHex("TextField.foreground", "#d4d4d4")
75
+ val borderColor = colorHex("Separator.foreground", "#3c3c3c")
76
+ val selectionBg = colorHex("List.selectionBackground", "#0e639c")
77
+ val selectionFg = colorHex("List.selectionForeground", "#ffffff")
78
+ val linkFg = colorHex("Link.activeForeground", "#4ec9b0")
79
+ val isDark = (javax.swing.UIManager.getColor("Panel.background")?.let {
80
+ (it.red * 299 + it.green * 587 + it.blue * 114) / 1000 < 128
81
+ } ?: true)
82
+ val colorScheme = if (isDark) "dark" else "light"
83
+
84
+ val themeJs = """
85
+ (function() {
86
+ var root = document.documentElement;
87
+ root.style.setProperty('--ide-bg', '${'$'}bg');
88
+ root.style.setProperty('--ide-fg', '${'$'}fg');
89
+ root.style.setProperty('--ide-input-bg', '${'$'}inputBg');
90
+ root.style.setProperty('--ide-input-fg', '${'$'}inputFg');
91
+ root.style.setProperty('--ide-border', '${'$'}borderColor');
92
+ root.style.setProperty('--ide-selection-bg', '${'$'}selectionBg');
93
+ root.style.setProperty('--ide-selection-fg', '${'$'}selectionFg');
94
+ root.style.setProperty('--ide-link', '${'$'}linkFg');
95
+ root.setAttribute('data-ide-theme', '${'$'}colorScheme');
96
+ root.style.colorScheme = '${'$'}colorScheme';
97
+ document.body.style.background = '${'$'}bg';
98
+ document.body.style.color = '${'$'}fg';
99
+ })();
100
+ """.trimIndent()
101
+ b.executeJavaScript(themeJs, b.url, 0)
102
+
103
+ val inject = jsQuery.inject(
104
+ "msg",
105
+ "function(r){}",
106
+ "function(code,msg){console.error('[unextension] bridge error',code,msg);}"
107
+ )
108
+ val bridgeJs = """
109
+ (function() {
110
+ window.__unextension_jb_bridge = function(msg) { $inject };
111
+ })();
112
+ """.trimIndent()
113
+ b.executeJavaScript(bridgeJs, b.url, 0)
114
+ }
115
+ }
116
+ browser.jbCefClient.addLoadHandler(loadHandler, browser.cefBrowser)
117
+
118
+ com.intellij.openapi.util.Disposer.register(toolWindow.disposable) {
119
+ browser.jbCefClient.removeLoadHandler(loadHandler, browser.cefBrowser)
120
+ jsQuery.dispose()
121
+ browser.dispose()
122
+ }
123
+
124
+ ${devMode
125
+ ? `toolWindow.setTitleActions(listOf(object : AnAction("Open DevTools", "Open browser DevTools", com.intellij.icons.AllIcons.Debugger.Console) {
126
+ override fun actionPerformed(e: AnActionEvent) { browser.openDevtools() }
127
+ }))`
128
+ : ''}
129
+ contentManager.addContent(contentManager.factory.createContent(browser.component, "", false))
130
+ }
131
+
132
+ ${actionFunctions}
133
+
134
+ private fun resolveWebviewHtml(): String? {
135
+ val tmpDir = java.io.File(System.getProperty("java.io.tmpdir"), "unextension-webview-${classNameLower}")
136
+ tmpDir.mkdirs()
137
+ val stream = this::class.java.getResourceAsStream("/webview/index.html") ?: return null
138
+ var html = stream.use { it.bufferedReader().readText() }
139
+ val route = if ("${routeRelative}".isEmpty()) "/" else "/${routeRelative}"
140
+ val routeScript = "<script>window.__UNEXTENSION_ROUTE__=" + org.json.JSONObject.quote(route) + ";</script>"
141
+ html = html.replace("</head>", "\${routeScript}</head>")
142
+ java.io.File(tmpDir, "index.html").writeText(html)
143
+ return java.io.File(tmpDir, "index.html").toURI().toString()
144
+ }
145
+
146
+ private fun runShellCommand(command: String, shell: String, flag: String): Triple<String, String, Int> {
147
+ return try {
148
+ val proc = ProcessBuilder(shell, flag, command)
149
+ proc.redirectErrorStream(false)
150
+ val process = proc.start()
151
+ val stdout = process.inputStream.bufferedReader().readText()
152
+ val stderr = process.errorStream.bufferedReader().readText()
153
+ val exitCode = process.waitFor()
154
+ Triple(stdout, stderr, exitCode)
155
+ } catch (e: Exception) {
156
+ Triple("", e.message ?: "Unknown error", 1)
157
+ }
158
+ }
159
+ }
160
+ `;
161
+ }
162
+ //# sourceMappingURL=toolwindow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolwindow.js","sourceRoot":"","sources":["../../../src/targets/jetbrains/toolwindow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAEpD,MAAM,UAAU,yBAAyB,CACvC,SAAiB,EACjB,KAAa,EACb,OAAgB;IAEhB,MAAM,aAAa,GAAG,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IACnF,MAAM,cAAc,GAAG,SAAS,CAAC,WAAW,EAAE,CAAA;IAC9C,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,qBAAqB,EAAE,CAAA;IAExF,OAAO;;;;;;;;;;;;;;;;;QAiBD,SAAS;;;;;;;;;;;;;;;;;;;;;2DAqB0C,cAAc;;;;;;;;EAQvE,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8DAmC8C,GAAG;8DACH,GAAG;oEACG,GAAG;oEACH,GAAG;kEACL,GAAG;wEACG,GAAG;wEACH,GAAG;gEACX,GAAG;+DACJ,GAAG;oDACd,GAAG;4DACK,GAAG;uDACR,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;UA2BhD,OAAO;QACL,CAAC,CAAC;;YAEF;QACA,CAAC,CAAC,EACN;;;;EAIN,eAAe;;;+FAG8E,cAAc;;;;2BAIlF,aAAa,2BAA2B,aAAa;;;;;;;;;;;;;;;;;;;;;CAqB/E,CAAA;AACD,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function toPascalCase(str: string): string;
2
+ export declare function defaultIconSvg(title: string): string;
3
+ export declare function defaultPluginIconSvg(title: string): string;
4
+ export declare function escapeXml(str: string): string;
5
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/targets/shared.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGpD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM1D;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO7C"}
@@ -0,0 +1,23 @@
1
+ export function toPascalCase(str) {
2
+ return str.replace(/(^|[-_])([a-z])/g, (_, __, c) => c.toUpperCase());
3
+ }
4
+ export function defaultIconSvg(title) {
5
+ const letter = (title?.[0] ?? '?').toUpperCase();
6
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><text x="12" y="16" text-anchor="middle" font-size="12" fill="currentColor" stroke="none">${letter}</text></svg>`;
7
+ }
8
+ export function defaultPluginIconSvg(title) {
9
+ const letter = (title?.[0] ?? '?').toUpperCase();
10
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
11
+ <rect width="40" height="40" rx="8" fill="#6c71c4"/>
12
+ <text x="20" y="27" text-anchor="middle" font-size="22" font-family="sans-serif" font-weight="bold" fill="#ffffff">${letter}</text>
13
+ </svg>`;
14
+ }
15
+ export function escapeXml(str) {
16
+ return str
17
+ .replace(/&/g, '&amp;')
18
+ .replace(/</g, '&lt;')
19
+ .replace(/>/g, '&gt;')
20
+ .replace(/"/g, '&quot;')
21
+ .replace(/'/g, '&apos;');
22
+ }
23
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/targets/shared.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,GAAG,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAA;AACvE,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,CAAA;IAChD,OAAO,2RAA2R,MAAM,eAAe,CAAA;AACzT,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,MAAM,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,CAAA;IAChD,OAAO;;uHAE8G,MAAM;OACtH,CAAA;AACP,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,OAAO,GAAG;SACP,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;AAC5B,CAAC"}
@@ -0,0 +1,23 @@
1
+ // @ts-check
2
+ // Declares the variables available in the generated extension.js scope.
3
+ // This file is NOT included in the output — it exists only for IDE type checking.
4
+
5
+ /** @type {typeof import('vscode')} */
6
+
7
+ const vscode = /** @type {any} */ (undefined)
8
+
9
+ /** @type {import('os')} */
10
+
11
+ const os = /** @type {any} */ (undefined)
12
+
13
+ /** @type {typeof import('child_process').execFile} */
14
+
15
+ const execFile = /** @type {any} */ (undefined)
16
+
17
+ /** @type {import('vscode').OutputChannel} */
18
+
19
+ const output = /** @type {any} */ (undefined)
20
+
21
+ /** @type {string} */
22
+
23
+ const extensionPath = /** @type {any} */ (undefined)
@@ -0,0 +1,11 @@
1
+ {
2
+ "compilerOptions": {
3
+ "checkJs": true,
4
+ "strict": true,
5
+ "noEmit": true,
6
+ "target": "ES2020",
7
+ "module": "CommonJS",
8
+ "globals": {}
9
+ },
10
+ "include": ["./*.js", "./globals.js"]
11
+ }
@@ -0,0 +1,27 @@
1
+ // @ts-check
2
+ /// <reference path="./globals.js" />
3
+
4
+ /**
5
+ * @param {{ pattern?: string } | null} payload
6
+ * @param {(result: string[]) => void} reply
7
+ * @param {import('vscode').OutputChannel} channel
8
+ * @returns {Promise<void>}
9
+ */
10
+ async function listProjectFiles(payload, reply, channel) {
11
+ const pattern = payload?.pattern ?? '**/*'
12
+ const folders = vscode.workspace.workspaceFolders
13
+ if (!folders || folders.length === 0) {
14
+ ;(channel || output).appendLine('[unextension] listProjectFiles: no workspace folders open')
15
+ reply([])
16
+ return
17
+ }
18
+ const uris = await vscode.workspace.findFiles(
19
+ new vscode.RelativePattern(folders[0], pattern),
20
+ '{**/node_modules/**,**/.git/**,**/.idea/**,**/.gradle/**,**/build/**,**/out/**,**/dist/**}',
21
+ )
22
+ const files = uris.map((u) => vscode.workspace.asRelativePath(u, false))
23
+ ;(channel || output).appendLine(
24
+ '[unextension] listProjectFiles: found ' + files.length + ' file(s) for pattern ' + pattern,
25
+ )
26
+ reply(files)
27
+ }
@@ -0,0 +1,17 @@
1
+ // @ts-check
2
+ /// <reference path="./globals.js" />
3
+
4
+ /**
5
+ * @param {{ message?: string, level?: 'info' | 'warning' | 'error' } | null} payload
6
+ * @param {(result: { shown: boolean }) => void} reply
7
+ * @param {import('vscode').OutputChannel} channel
8
+ * @returns {Promise<void>}
9
+ */
10
+ async function notify(payload, reply, channel) {
11
+ const level = payload?.level ?? 'info'
12
+ const message = payload?.message ?? ''
13
+ if (level === 'error') vscode.window.showErrorMessage(message)
14
+ else if (level === 'warning') vscode.window.showWarningMessage(message)
15
+ else vscode.window.showInformationMessage(message)
16
+ reply({ shown: true })
17
+ }
@@ -0,0 +1,24 @@
1
+ // @ts-check
2
+ /// <reference path="./globals.js" />
3
+
4
+ /**
5
+ * @param {{ path?: string } | null} payload
6
+ * @param {(result: { content: string, encoding: 'utf8' }) => void} reply
7
+ * @param {import('vscode').OutputChannel} channel
8
+ * @returns {Promise<void>}
9
+ */
10
+ async function readProjectFile(payload, reply, channel) {
11
+ const filePath = payload?.path ?? ''
12
+ const folders = vscode.workspace.workspaceFolders
13
+ if (!folders || folders.length === 0) {
14
+ reply({ content: '', encoding: 'utf8' })
15
+ return
16
+ }
17
+ const uri = vscode.Uri.joinPath(folders[0].uri, filePath)
18
+ const bytes = await vscode.workspace.fs.readFile(uri)
19
+ const content = Buffer.from(bytes).toString('utf8')
20
+ ;(channel || output).appendLine(
21
+ '[unextension] readProjectFile: ' + filePath + ' (' + bytes.length + ' bytes)',
22
+ )
23
+ reply({ content, encoding: 'utf8' })
24
+ }
@@ -0,0 +1,42 @@
1
+ // @ts-check
2
+ /// <reference path="./globals.js" />
3
+
4
+ /**
5
+ * @param {{ command?: string, shell?: 'cmd' | 'powershell' | 'bash' | 'sh' | 'zsh' | 'fish' } | null} payload
6
+ * @param {(result: { command: string, stdout: string, stderr: string, exitCode: number }) => void} reply
7
+ * @param {import('vscode').OutputChannel} channel
8
+ * @returns {void}
9
+ */
10
+ function runCommand(payload, reply, channel) {
11
+ const command = payload?.command ?? ''
12
+ const isWin = os.platform() === 'win32'
13
+ const requestedShell = payload?.shell
14
+ const [shell, flag] = resolveShell(requestedShell, isWin)
15
+ execFile(shell, [flag, command], { timeout: 30000 }, (err, stdout, stderr) => {
16
+ reply({ command, stdout, stderr, exitCode: err?.code ?? 0 })
17
+ })
18
+ }
19
+
20
+ /**
21
+ * @param {string | undefined} requested
22
+ * @param {boolean} isWin
23
+ * @returns {[string, string]}
24
+ */
25
+ function resolveShell(requested, isWin) {
26
+ switch (requested) {
27
+ case 'cmd':
28
+ return ['cmd', '/c']
29
+ case 'powershell':
30
+ return ['powershell', '-Command']
31
+ case 'bash':
32
+ return ['bash', '-c']
33
+ case 'sh':
34
+ return ['sh', '-c']
35
+ case 'zsh':
36
+ return ['zsh', '-c']
37
+ case 'fish':
38
+ return ['fish', '-c']
39
+ default:
40
+ return isWin ? ['cmd', '/c'] : ['sh', '-c']
41
+ }
42
+ }
@@ -0,0 +1,39 @@
1
+ // @ts-check
2
+ /// <reference path="./globals.js" />
3
+
4
+ /**
5
+ * @param {{ name?: string, payload?: unknown } | null} payload
6
+ * @param {(result: { result: unknown, exitCode: number, stderr: string }) => void} reply
7
+ * @param {import('vscode').OutputChannel} channel
8
+ * @returns {void}
9
+ */
10
+ function runScript(payload, reply, channel) {
11
+ const name = payload?.name ?? ''
12
+ const scriptPayload = payload?.payload ?? null
13
+ const scriptsDir = path.join(extensionPath, 'scripts')
14
+ const scriptFile = path.join(scriptsDir, name.endsWith('.js') ? name : name + '.js')
15
+
16
+ if (!fs.existsSync(scriptFile)) {
17
+ ;(channel || output).appendLine('[unextension] runScript: script not found: ' + scriptFile)
18
+ reply({ result: null, exitCode: 1, stderr: 'Script not found: ' + name })
19
+ return
20
+ }
21
+
22
+ const input = JSON.stringify(scriptPayload ?? null)
23
+ ;(channel || output).appendLine('[unextension] runScript: running ' + name)
24
+
25
+ execFile(
26
+ process.execPath,
27
+ [scriptFile],
28
+ { timeout: 30000, env: { ...process.env, UNEXTENSION_PAYLOAD: input } },
29
+ (err, stdout, stderr) => {
30
+ let result
31
+ try {
32
+ result = JSON.parse(stdout.trim())
33
+ } catch {
34
+ result = stdout.trim()
35
+ }
36
+ reply({ result, exitCode: err?.code ?? 0, stderr })
37
+ },
38
+ )
39
+ }
@@ -0,0 +1,24 @@
1
+ // @ts-check
2
+ /// <reference path="./globals.js" />
3
+
4
+ /**
5
+ * @param {{ path?: string, content?: string } | null} payload
6
+ * @param {(result: { success: boolean }) => void} reply
7
+ * @param {import('vscode').OutputChannel} channel
8
+ * @returns {Promise<void>}
9
+ */
10
+ async function writeProjectFile(payload, reply, channel) {
11
+ const filePath = payload?.path ?? ''
12
+ const content = payload?.content ?? ''
13
+ const folders = vscode.workspace.workspaceFolders
14
+ if (!folders || folders.length === 0) {
15
+ reply({ success: false })
16
+ return
17
+ }
18
+ const uri = vscode.Uri.joinPath(folders[0].uri, filePath)
19
+ await vscode.workspace.fs.writeFile(uri, Buffer.from(content, 'utf8'))
20
+ ;(channel || output).appendLine(
21
+ '[unextension] writeProjectFile: ' + filePath + ' (' + content.length + ' chars)',
22
+ )
23
+ reply({ success: true })
24
+ }
@@ -0,0 +1,2 @@
1
+ export declare function generateActions(): string;
2
+ //# sourceMappingURL=actions.d.ts.map