@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
|
@@ -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 @@
|
|
|
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, '&')
|
|
18
|
+
.replace(/</g, '<')
|
|
19
|
+
.replace(/>/g, '>')
|
|
20
|
+
.replace(/"/g, '"')
|
|
21
|
+
.replace(/'/g, ''');
|
|
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,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
|
+
}
|