@lvce-editor/shared-process 0.0.2
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/LICENSE +21 -0
- package/README.md +41 -0
- package/bin/sharedProcess.js +3 -0
- package/index.js +6 -0
- package/package.json +96 -0
- package/src/parts/Assert/Assert.js +57 -0
- package/src/parts/Callback/Callback.js +48 -0
- package/src/parts/ChildProcess/ChildProcess.js +98 -0
- package/src/parts/ClipBoard/ClipBoard.ipc.js +7 -0
- package/src/parts/ClipBoard/ClipBoard.js +23 -0
- package/src/parts/ClipBoard/ClipBoardGnome.js +78 -0
- package/src/parts/ClipBoard/ClipBoardNoop.js +14 -0
- package/src/parts/ClipBoard/ClipBoardWindows.js +22 -0
- package/src/parts/Command/Command.js +264 -0
- package/src/parts/Configuration/Configuration.ipc.js +7 -0
- package/src/parts/Configuration/Configuration.js +39 -0
- package/src/parts/Credentials/Credentials.ipc.js +10 -0
- package/src/parts/Credentials/Credentials.js +69 -0
- package/src/parts/Debug/Debug.js +3 -0
- package/src/parts/Developer/Developer.ipc.js +20 -0
- package/src/parts/Developer/Developer.js +132 -0
- package/src/parts/Download/Download.js +33 -0
- package/src/parts/Electron/Electron.ipc.js +19 -0
- package/src/parts/Electron/Electron.js +99 -0
- package/src/parts/Env/Env.js +3 -0
- package/src/parts/Error/Error.js +30 -0
- package/src/parts/ErrorHandling/ErrorHandling.js +40 -0
- package/src/parts/Exec/Exec.js +16 -0
- package/src/parts/Extension/Extension.js +7 -0
- package/src/parts/ExtensionHost/ExtensionHost.ipc.js +80 -0
- package/src/parts/ExtensionHost/ExtensionHost.js +163 -0
- package/src/parts/ExtensionHost/ExtensionHostBraceCompletion.js +14 -0
- package/src/parts/ExtensionHost/ExtensionHostClosingTag.js +14 -0
- package/src/parts/ExtensionHost/ExtensionHostColorTheme.js +60 -0
- package/src/parts/ExtensionHost/ExtensionHostCommand.js +19 -0
- package/src/parts/ExtensionHost/ExtensionHostCompletion.js +12 -0
- package/src/parts/ExtensionHost/ExtensionHostDefinition.js +12 -0
- package/src/parts/ExtensionHost/ExtensionHostDiagnostic.js +7 -0
- package/src/parts/ExtensionHost/ExtensionHostFileSystem.js +23 -0
- package/src/parts/ExtensionHost/ExtensionHostFormatting.js +4 -0
- package/src/parts/ExtensionHost/ExtensionHostHover.js +12 -0
- package/src/parts/ExtensionHost/ExtensionHostIconTheme.js +35 -0
- package/src/parts/ExtensionHost/ExtensionHostImplementation.js +12 -0
- package/src/parts/ExtensionHost/ExtensionHostKeyBindings.js +23 -0
- package/src/parts/ExtensionHost/ExtensionHostLanguages.js +84 -0
- package/src/parts/ExtensionHost/ExtensionHostManagement.js +26 -0
- package/src/parts/ExtensionHost/ExtensionHostOutput.js +3 -0
- package/src/parts/ExtensionHost/ExtensionHostQuickPick.js +6 -0
- package/src/parts/ExtensionHost/ExtensionHostReference.js +12 -0
- package/src/parts/ExtensionHost/ExtensionHostRename.js +27 -0
- package/src/parts/ExtensionHost/ExtensionHostSemanticTokens.js +10 -0
- package/src/parts/ExtensionHost/ExtensionHostSourceControl.js +15 -0
- package/src/parts/ExtensionHost/ExtensionHostStats.js +3 -0
- package/src/parts/ExtensionHost/ExtensionHostStatusBar.js +7 -0
- package/src/parts/ExtensionHost/ExtensionHostTabCompletion.js +12 -0
- package/src/parts/ExtensionHost/ExtensionHostTextDocument.js +35 -0
- package/src/parts/ExtensionHost/ExtensionHostTypeDefinition.js +12 -0
- package/src/parts/ExtensionHost/ExtensionHostWorkspace.js +3 -0
- package/src/parts/ExtensionManagement/ExtensionManagement.ipc.js +11 -0
- package/src/parts/ExtensionManagement/ExtensionManagement.js +319 -0
- package/src/parts/FileSystem/FileSystem.ipc.js +32 -0
- package/src/parts/FileSystem/FileSystem.js +228 -0
- package/src/parts/Git/Git.js +58 -0
- package/src/parts/Json/Json.js +67 -0
- package/src/parts/JsonFile/JsonFile.js +14 -0
- package/src/parts/JsonRpc/JsonRpc.js +22 -0
- package/src/parts/Native/Native.ipc.js +6 -0
- package/src/parts/Native/Native.js +14 -0
- package/src/parts/OutputChannel/OutputChannel.ipc.js +51 -0
- package/src/parts/OutputChannel/OutputChannel.js +37 -0
- package/src/parts/ParentIpc/ParentIpc.js +203 -0
- package/src/parts/Path/Path.js +17 -0
- package/src/parts/Platform/Platform.ipc.js +18 -0
- package/src/parts/Platform/Platform.js +165 -0
- package/src/parts/Preferences/Preferences.ipc.js +6 -0
- package/src/parts/Preferences/Preferences.js +75 -0
- package/src/parts/PrettyError/PrettyError.js +86 -0
- package/src/parts/Process/Process.ipc.js +7 -0
- package/src/parts/Process/Process.js +7 -0
- package/src/parts/Queue/Queue.js +35 -0
- package/src/parts/RequiresSocket/RequiresSocket.js +11 -0
- package/src/parts/RgPath/RgPath.js +1 -0
- package/src/parts/Root/Root.js +6 -0
- package/src/parts/Search/Search.ipc.js +6 -0
- package/src/parts/Search/Search.js +101 -0
- package/src/parts/SearchFile/SearchFile.ipc.js +6 -0
- package/src/parts/SearchFile/SearchFile.js +51 -0
- package/src/parts/Socket/Socket.js +132 -0
- package/src/parts/Stats/Stats.js +45 -0
- package/src/parts/Terminal/Terminal.ipc.js +45 -0
- package/src/parts/Terminal/Terminal.js +161 -0
- package/src/parts/TextDocument/TextDocument.ipc.js +6 -0
- package/src/parts/TextDocument/TextDocument.js +37 -0
- package/src/parts/Trace/Trace.js +15 -0
- package/src/parts/Trash/Trash.js +11 -0
- package/src/parts/WebSocketServer/WebSocketServer.ipc.js +6 -0
- package/src/parts/WebSocketServer/WebSocketServer.js +43 -0
- package/src/parts/Workspace/Workspace.ipc.js +7 -0
- package/src/parts/Workspace/Workspace.js +107 -0
- package/src/sharedProcessMain.js +61 -0
- package/test/Callback.test.js +42 -0
- package/test/ClipBoard.test.js +105 -0
- package/test/Credentials.test.js +147 -0
- package/test/Developer.test.js +34 -0
- package/test/Electron.test.js +93 -0
- package/test/Error.test.js +16 -0
- package/test/Exec.test.js +25 -0
- package/test/ExtensionHost.test.js +127 -0
- package/test/ExtensionHostColorTheme.test.js +141 -0
- package/test/ExtensionHostCommand.test.js +27 -0
- package/test/ExtensionHostCompletion.test.js +45 -0
- package/test/ExtensionHostDefinition.test.js +35 -0
- package/test/ExtensionHostDiagnostic.test.js +23 -0
- package/test/ExtensionHostFileSystem.test.js +91 -0
- package/test/ExtensionHostFormatting.test.js +27 -0
- package/test/ExtensionHostIconTheme.test.js +73 -0
- package/test/ExtensionHostLanguages.test.js +212 -0
- package/test/ExtensionHostOutput.test.js +23 -0
- package/test/ExtensionHostRename.test.js +59 -0
- package/test/ExtensionHostSemanticTokens.test.js +26 -0
- package/test/ExtensionHostTabCompletion.test.js +35 -0
- package/test/ExtensionHostTextDocument.test.js +61 -0
- package/test/ExtensionHostTypeDefinition.test.js +35 -0
- package/test/ExtensionHostWorkspace.test.js +21 -0
- package/test/ExtensionManagement.test.js +408 -0
- package/test/FileSystem.test.js +294 -0
- package/test/Json.test.js +25 -0
- package/test/Native.test.js +31 -0
- package/test/OutputChannel.test.js +68 -0
- package/test/Path.test.js +17 -0
- package/test/Platform.test.js +61 -0
- package/test/Preferences.test.js +140 -0
- package/test/Process.test.js +9 -0
- package/test/RgPath.test.js +5 -0
- package/test/Search.test.js +45 -0
- package/test/SearchFile.test.js +80 -0
- package/test/Terminal.test.js +53 -0
- package/test/Trash.test.js +34 -0
- package/test/WebSocketServer.test.js +98 -0
- package/test/fixture-search-1/index.html +10 -0
- package/test/fixture-search-file-1/fileA +0 -0
- package/test/fixture-search-file-1/fileB +0 -0
- package/test/fixture-search-file-1/nested/fileC +0 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// TODO lazyload chokidar and trash (but doesn't work currently because of bug with jest)
|
|
2
|
+
import * as fs from 'node:fs/promises'
|
|
3
|
+
import * as os from 'node:os'
|
|
4
|
+
import chokidar from 'chokidar'
|
|
5
|
+
import * as Error from '../Error/Error.js'
|
|
6
|
+
import * as Path from '../Path/Path.js'
|
|
7
|
+
import * as Trash from '../Trash/Trash.js'
|
|
8
|
+
import * as Platform from '../Platform/Platform.js'
|
|
9
|
+
|
|
10
|
+
export const state = {
|
|
11
|
+
watcherMap: Object.create(null),
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const copy = async (source, target) => {
|
|
15
|
+
const { copy } = await import('fs-extra')
|
|
16
|
+
try {
|
|
17
|
+
await copy(source, target, { recursive: true })
|
|
18
|
+
} catch (error) {
|
|
19
|
+
throw new Error.OperationalError({
|
|
20
|
+
cause: error,
|
|
21
|
+
code: 'E_FILE_SYSTEM_ERROR',
|
|
22
|
+
message: `Failed to copy "${source}" to "${target}"`,
|
|
23
|
+
category: 'File System Error',
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const readFile = async (path) => {
|
|
29
|
+
try {
|
|
30
|
+
const content = await fs.readFile(path, 'utf-8')
|
|
31
|
+
return content
|
|
32
|
+
} catch (error) {
|
|
33
|
+
throw new Error.OperationalError({
|
|
34
|
+
cause: error,
|
|
35
|
+
code: 'E_FILE_SYSTEM_ERROR',
|
|
36
|
+
message: `Failed to read file "${path}"`,
|
|
37
|
+
category: 'File System Error',
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const writeFile = async (path, content) => {
|
|
43
|
+
try {
|
|
44
|
+
// queue would be more correct for concurrent writes but also slower
|
|
45
|
+
// Queue.add(`writeFile/${path}`, () =>
|
|
46
|
+
await fs.writeFile(path, content)
|
|
47
|
+
} catch (error) {
|
|
48
|
+
throw new Error.OperationalError({
|
|
49
|
+
cause: error,
|
|
50
|
+
code: 'E_FILE_SYSTEM_ERROR',
|
|
51
|
+
message: `Failed to write to file "${path}"`,
|
|
52
|
+
category: 'File System Error',
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const ensureFile = async (path, content) => {
|
|
58
|
+
try {
|
|
59
|
+
await fs.mkdir(Path.dirname(path), { recursive: true })
|
|
60
|
+
await fs.writeFile(path, content)
|
|
61
|
+
} catch (error) {
|
|
62
|
+
throw new Error.OperationalError({
|
|
63
|
+
cause: error,
|
|
64
|
+
code: 'E_FILE_SYSTEM_ERROR',
|
|
65
|
+
message: `Failed to write to file "${path}"`,
|
|
66
|
+
category: 'File System Error',
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const createFile = async (path) => {
|
|
72
|
+
try {
|
|
73
|
+
await fs.writeFile(path, '', { flag: 'wx' })
|
|
74
|
+
} catch (error) {
|
|
75
|
+
throw new Error.OperationalError({
|
|
76
|
+
cause: error,
|
|
77
|
+
code: 'E_FILE_SYSTEM_ERROR',
|
|
78
|
+
message: `Failed to create file "${path}"`,
|
|
79
|
+
category: 'File System Error',
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const createFolder = async (path, options) => {
|
|
85
|
+
try {
|
|
86
|
+
await fs.mkdir(path, options)
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw new Error.OperationalError({
|
|
89
|
+
cause: error,
|
|
90
|
+
code: 'E_FILE_SYSTEM_ERROR',
|
|
91
|
+
message: `Failed to create folder "${path}"`,
|
|
92
|
+
category: 'File System Error',
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const isOkayToRemove = (path) => {
|
|
98
|
+
if (path === '/') {
|
|
99
|
+
return false
|
|
100
|
+
}
|
|
101
|
+
if (path === '~') {
|
|
102
|
+
return false
|
|
103
|
+
}
|
|
104
|
+
if (path === os.homedir()) {
|
|
105
|
+
return false
|
|
106
|
+
}
|
|
107
|
+
return true
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const remove = async (path) => {
|
|
111
|
+
if (!isOkayToRemove(path)) {
|
|
112
|
+
console.warn('not removing path')
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// TODO lazyload trash (doesn't work currently because of bug with jest)
|
|
117
|
+
// const { trash } = await import('../../wrap/trash.js')
|
|
118
|
+
try {
|
|
119
|
+
await Trash.trash(path)
|
|
120
|
+
} catch (error) {
|
|
121
|
+
throw new Error.OperationalError({
|
|
122
|
+
cause: error,
|
|
123
|
+
code: 'E_FILE_REMOVE_SYSTEM_ERROR',
|
|
124
|
+
message: `Failed to remove "${path}"`,
|
|
125
|
+
category: 'File System Error',
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export const exists = async (path) => {
|
|
131
|
+
try {
|
|
132
|
+
await fs.access(path)
|
|
133
|
+
return true
|
|
134
|
+
} catch {
|
|
135
|
+
return false
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const getType = (dirent) => {
|
|
140
|
+
if (dirent.isFile()) {
|
|
141
|
+
return 'file'
|
|
142
|
+
}
|
|
143
|
+
if (dirent.isDirectory()) {
|
|
144
|
+
return 'directory'
|
|
145
|
+
}
|
|
146
|
+
return 'unknown'
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const toPrettyDirent = (dirent) => {
|
|
150
|
+
return {
|
|
151
|
+
name: dirent.name,
|
|
152
|
+
type: getType(dirent),
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export const readDirWithFileTypes = async (path) => {
|
|
157
|
+
try {
|
|
158
|
+
const dirents = await fs.readdir(path, { withFileTypes: true })
|
|
159
|
+
const prettyDirents = dirents.map(toPrettyDirent)
|
|
160
|
+
return prettyDirents
|
|
161
|
+
} catch (error) {
|
|
162
|
+
throw new Error.OperationalError({
|
|
163
|
+
cause: error,
|
|
164
|
+
code: 'E_FILE_READ_DIR_SYSTEM_ERROR',
|
|
165
|
+
message: `Failed to read directory "${path}"`,
|
|
166
|
+
category: 'File System Error',
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export const mkdir = async (path) => {
|
|
172
|
+
try {
|
|
173
|
+
await fs.mkdir(path)
|
|
174
|
+
} catch (error) {
|
|
175
|
+
throw new Error.OperationalError({
|
|
176
|
+
cause: error,
|
|
177
|
+
code: 'E_CREATE_DIRECTORY_FAILED',
|
|
178
|
+
message: `Failed to create directory "${path}"`,
|
|
179
|
+
category: 'File System Error',
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export const rename = async (oldPath, newPath) => {
|
|
185
|
+
try {
|
|
186
|
+
await fs.rename(oldPath, newPath)
|
|
187
|
+
} catch (error) {
|
|
188
|
+
throw new Error.OperationalError({
|
|
189
|
+
cause: error,
|
|
190
|
+
code: 'E_RENAME_FAILED',
|
|
191
|
+
message: `Failed to rename "${oldPath}" to "${newPath}"`,
|
|
192
|
+
category: 'File System Error',
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export const watch = (path, options) => {
|
|
198
|
+
// let state = 'loading'
|
|
199
|
+
|
|
200
|
+
const watcher = chokidar.watch(`${path}`, {
|
|
201
|
+
ignoreInitial: true,
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
if (options.onAll) {
|
|
205
|
+
watcher.on('all', options.onAll)
|
|
206
|
+
}
|
|
207
|
+
return watcher
|
|
208
|
+
|
|
209
|
+
// const { default: chokidar } = await import('chokidar')
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export const getPathSeparator = () => {
|
|
213
|
+
return Platform.getPathSeparator()
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// export const unwatch = (id) => {
|
|
217
|
+
// state.watchers[id].close()
|
|
218
|
+
// delete state.watchers[id]
|
|
219
|
+
// }
|
|
220
|
+
|
|
221
|
+
// export const unwatchAll = () => {
|
|
222
|
+
// for (const key in state.watchers) {
|
|
223
|
+
// unwatch(key)
|
|
224
|
+
// }
|
|
225
|
+
// }
|
|
226
|
+
|
|
227
|
+
// TODO ui should show useful error message when file cannot be saved
|
|
228
|
+
// and for permission denied error it should use that npm module that allows root
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export const id = 'git'
|
|
2
|
+
|
|
3
|
+
export const label = 'Git'
|
|
4
|
+
|
|
5
|
+
export const acceptInputCommand = 'git.commit'
|
|
6
|
+
|
|
7
|
+
const RE_ONLY_WHITESPACE = /^\s+$/
|
|
8
|
+
|
|
9
|
+
export const validateInput = (text) => {
|
|
10
|
+
if (RE_ONLY_WHITESPACE.test(text)) {
|
|
11
|
+
return {
|
|
12
|
+
message: 'Current commit message only contains whitespace characters',
|
|
13
|
+
type: 'warning',
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return undefined
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const vscode = {}
|
|
20
|
+
|
|
21
|
+
export const activate = () => {
|
|
22
|
+
const sourceControlProvider = vscode.createSourceControlProvider({
|
|
23
|
+
providerId: 'git',
|
|
24
|
+
label: 'Git',
|
|
25
|
+
acceptInput(text) {},
|
|
26
|
+
validateInput,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const mergeGroup = vscode.createSourceControlResourceGroup({
|
|
30
|
+
providerId: 'git',
|
|
31
|
+
groupId: 'merge',
|
|
32
|
+
label: 'Merge Changes',
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const indexGroup = vscode.createSourceControlResourceGroup({
|
|
36
|
+
providerId: 'git',
|
|
37
|
+
groupId: 'index',
|
|
38
|
+
label: 'Staged Changes',
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const workingTreeGroup = vscode.createSourceControlResourceGroup({
|
|
42
|
+
providerId: 'git',
|
|
43
|
+
groupId: 'workingTree',
|
|
44
|
+
label: 'Untracked Changes',
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const untrackedGroup = vscode.createSourceControlResourceGroup({
|
|
48
|
+
providerId: 'git',
|
|
49
|
+
groupId: 'untracked',
|
|
50
|
+
label: 'Untracked Changes',
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
sourceControlProvider.setCount(111)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const deactivate = () => {
|
|
57
|
+
vscode.unregisterSourceControlProvider('git')
|
|
58
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import fallback from 'json-parse-even-better-errors'
|
|
2
|
+
import { codeFrameColumns } from '@babel/code-frame'
|
|
3
|
+
import { LinesAndColumns } from 'lines-and-columns'
|
|
4
|
+
|
|
5
|
+
// parsing error handling based on https://github.com/sindresorhus/parse-json/blob/main/index.js
|
|
6
|
+
|
|
7
|
+
export const parse = async (string, filePath) => {
|
|
8
|
+
try {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(string)
|
|
11
|
+
} catch (error) {
|
|
12
|
+
fallback(string)
|
|
13
|
+
throw error
|
|
14
|
+
}
|
|
15
|
+
} catch (error) {
|
|
16
|
+
error.message = error.message.replace(/\n/g, '')
|
|
17
|
+
const indexMatch = error.message.match(
|
|
18
|
+
/in JSON at position (\d+) while parsing/
|
|
19
|
+
)
|
|
20
|
+
let topMessage = error.message
|
|
21
|
+
let bottomMessage = error.message
|
|
22
|
+
if (topMessage.startsWith('Unexpected token')) {
|
|
23
|
+
topMessage = 'JSON parsing error:'
|
|
24
|
+
}
|
|
25
|
+
if (bottomMessage.includes('while parsing "{')) {
|
|
26
|
+
bottomMessage = bottomMessage.slice(
|
|
27
|
+
0,
|
|
28
|
+
bottomMessage.indexOf('while parsing "{')
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
if (bottomMessage.includes(' (0x7D) ')) {
|
|
32
|
+
bottomMessage = bottomMessage.replace(' (0x7D) ', ' ')
|
|
33
|
+
}
|
|
34
|
+
bottomMessage = bottomMessage.trim()
|
|
35
|
+
const jsonError = new Error(topMessage)
|
|
36
|
+
if (indexMatch && indexMatch.length > 0) {
|
|
37
|
+
const lines = new LinesAndColumns(string)
|
|
38
|
+
const index = Number(indexMatch[1])
|
|
39
|
+
const location = lines.locationForIndex(index)
|
|
40
|
+
const codeFrame = codeFrameColumns(
|
|
41
|
+
string,
|
|
42
|
+
{ start: { line: location.line + 1, column: location.column + 1 } },
|
|
43
|
+
{ highlightCode: true }
|
|
44
|
+
)
|
|
45
|
+
jsonError.codeFrame = codeFrame
|
|
46
|
+
}
|
|
47
|
+
// console.log({ bottomMessage, message: error.message })
|
|
48
|
+
jsonError.stack = `${bottomMessage}\n at ${filePath}`
|
|
49
|
+
throw jsonError
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// try {
|
|
53
|
+
// return JSON.parse(json)
|
|
54
|
+
// } catch (error) {
|
|
55
|
+
// const parseJsonBetterErrors = await import('parse-json')
|
|
56
|
+
// try {
|
|
57
|
+
// return parseJsonBetterErrors.default(json, filePath)
|
|
58
|
+
// } catch (error) {
|
|
59
|
+
// console.log({ frame: error.codeFrame })
|
|
60
|
+
// throw error
|
|
61
|
+
// }
|
|
62
|
+
// }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const stringify = (value) => {
|
|
66
|
+
return JSON.stringify(value, null, 2) + '\n'
|
|
67
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
|
2
|
+
import * as Path from '../Path/Path.js'
|
|
3
|
+
import * as Json from '../Json/Json.js'
|
|
4
|
+
|
|
5
|
+
export const readJson = async (absolutePath) => {
|
|
6
|
+
const content = await readFile(absolutePath, 'utf-8')
|
|
7
|
+
const json = await Json.parse(content, absolutePath)
|
|
8
|
+
return json
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const writeJson = async (absolutePath, value) => {
|
|
12
|
+
await mkdir(Path.dirname(absolutePath), { recursive: true })
|
|
13
|
+
await writeFile(absolutePath, Json.stringify(value))
|
|
14
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as Callback from '../Callback/Callback.js'
|
|
2
|
+
|
|
3
|
+
export const send = (transport, method, ...params) => {
|
|
4
|
+
transport.send({
|
|
5
|
+
jsonrpc: '2.0',
|
|
6
|
+
method,
|
|
7
|
+
params,
|
|
8
|
+
})
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const invoke = (transport, method, ...params) => {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
// TODO use one map instead of two
|
|
14
|
+
const callbackId = Callback.register(resolve, reject)
|
|
15
|
+
transport.send({
|
|
16
|
+
jsonrpc: '2.0',
|
|
17
|
+
method,
|
|
18
|
+
params,
|
|
19
|
+
id: callbackId,
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import open from 'open'
|
|
2
|
+
import * as Error from '../Error/Error.js'
|
|
3
|
+
|
|
4
|
+
export const openFolder = async (path) => {
|
|
5
|
+
try {
|
|
6
|
+
await open(path)
|
|
7
|
+
} catch (error) {
|
|
8
|
+
throw new Error.OperationalError({
|
|
9
|
+
cause: error,
|
|
10
|
+
code: 'E_OPEN_SYSTEM_ERROR',
|
|
11
|
+
message: `Failed to open ${path}`,
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as Assert from '../Assert/Assert.js'
|
|
2
|
+
import * as Command from '../Command/Command.js'
|
|
3
|
+
import * as OutputChannel from './OutputChannel.js'
|
|
4
|
+
|
|
5
|
+
export const state = {
|
|
6
|
+
outputChannels: Object.create(null)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const open = (socket, id, path) => {
|
|
10
|
+
console.log({path})
|
|
11
|
+
Assert.object(socket)
|
|
12
|
+
// Assert.string(id)
|
|
13
|
+
Assert.string(path )
|
|
14
|
+
if (!socket) {
|
|
15
|
+
console.warn('socket not available')
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
if (state.outputChannels[id]) {
|
|
19
|
+
OutputChannel.dispose(state.outputChannels[id])
|
|
20
|
+
}
|
|
21
|
+
const onData = (data) => {
|
|
22
|
+
console.log('send data', data)
|
|
23
|
+
socket.send(
|
|
24
|
+
{
|
|
25
|
+
jsonrpc: '2.0',
|
|
26
|
+
method: 2133,
|
|
27
|
+
params: ['Output', 'handleData', data],
|
|
28
|
+
}
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
const onError = (error) => {
|
|
32
|
+
console.info(`[shared process] output channel error: ${error}`)
|
|
33
|
+
socket.send(
|
|
34
|
+
{
|
|
35
|
+
jsonrpc: '2.0',
|
|
36
|
+
method: 2133,
|
|
37
|
+
params: ['Output', 'handleError', error],
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
state.outputChannels[id] = OutputChannel.open(path, onData, onError)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const close = (socket, id) => {
|
|
44
|
+
OutputChannel.dispose(state.outputChannels[id])
|
|
45
|
+
delete state.outputChannels[id]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const __initialize__ = () => {
|
|
49
|
+
Command.register('OutputChannel.open', open)
|
|
50
|
+
Command.register('OutputChannel.close', close)
|
|
51
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Tail } from 'tail'
|
|
2
|
+
|
|
3
|
+
// TODO
|
|
4
|
+
// option 1: use tail child process
|
|
5
|
+
// option 2: https://www.npmjs.com/package/@logdna/tail-file
|
|
6
|
+
// option 3: node tail
|
|
7
|
+
|
|
8
|
+
export const open = (path, onData, onError) => {
|
|
9
|
+
let tail
|
|
10
|
+
try {
|
|
11
|
+
tail = new Tail(path, {
|
|
12
|
+
separator: null,
|
|
13
|
+
fromBeginning: true,
|
|
14
|
+
})
|
|
15
|
+
} catch (error) {
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
onError(error.toString())
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (tail) {
|
|
21
|
+
tail.on('line', onData)
|
|
22
|
+
|
|
23
|
+
tail.on('error', (error) => {
|
|
24
|
+
onError(error.toString())
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
const state = {
|
|
28
|
+
tail,
|
|
29
|
+
}
|
|
30
|
+
return state
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const dispose = (state) => {
|
|
34
|
+
if (state.tail) {
|
|
35
|
+
state.tail.unwatch()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { MessagePort, parentPort } from 'node:worker_threads'
|
|
2
|
+
import * as Callback from '../Callback/Callback.js'
|
|
3
|
+
import * as Command from '../Command/Command.js'
|
|
4
|
+
import * as Debug from '../Debug/Debug.js'
|
|
5
|
+
import * as Error from '../Error/Error.js'
|
|
6
|
+
import { requiresSocket } from '../RequiresSocket/RequiresSocket.js'
|
|
7
|
+
|
|
8
|
+
// TODO add tests for this
|
|
9
|
+
|
|
10
|
+
// TODO handle structure: one shared process multiple extension hosts
|
|
11
|
+
|
|
12
|
+
// TODO pass socket / port handle also in electron
|
|
13
|
+
|
|
14
|
+
export const state = {
|
|
15
|
+
electronPortMap: new Map(),
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const electronSend = (message) => {
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
// process.send(message)
|
|
21
|
+
parentPort.postMessage(message)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const handleMessageFromParentProcess = async (message, handle) => {
|
|
25
|
+
if (handle) {
|
|
26
|
+
handle.on('error', (error) => {
|
|
27
|
+
if (error && error.code === 'ECONNRESET') {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
console.info('[info shared process: handle error]', error)
|
|
31
|
+
})
|
|
32
|
+
Command.execute(
|
|
33
|
+
/* WebSocketServer.handleUpgrade */ 5621,
|
|
34
|
+
/* message */ message,
|
|
35
|
+
/* handle */ handle
|
|
36
|
+
)
|
|
37
|
+
} else if (message.result) {
|
|
38
|
+
Callback.resolve(message.id, message.result)
|
|
39
|
+
} else if (message.method) {
|
|
40
|
+
console.log({ message })
|
|
41
|
+
if (message.id) {
|
|
42
|
+
try {
|
|
43
|
+
console.log('INISDE HERE')
|
|
44
|
+
const result = requiresSocket(message.method)
|
|
45
|
+
? await Command.invoke(message.method, handle, ...message.params)
|
|
46
|
+
: await Command.invoke(message.method, ...message.params)
|
|
47
|
+
electronSend({
|
|
48
|
+
jsonrpc: '2.0',
|
|
49
|
+
id: message.id,
|
|
50
|
+
result: result ?? null,
|
|
51
|
+
})
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('[shared process] command failed to execute')
|
|
54
|
+
console.error(error)
|
|
55
|
+
electronSend({
|
|
56
|
+
jsonrpc: '2.0',
|
|
57
|
+
code: /* ExpectedError */ -32000,
|
|
58
|
+
id: message.id,
|
|
59
|
+
error: 'ExpectedError',
|
|
60
|
+
// @ts-ignore
|
|
61
|
+
data: error.toString(),
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
// TODO handle error
|
|
66
|
+
Command.execute(message.method, null, ...message.params)
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
console.warn('unknown message', message)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
*
|
|
75
|
+
* @param {{initialize:{port: MessagePort, folder:string}}} initializeMessage
|
|
76
|
+
*/
|
|
77
|
+
const electronInitialize = (initializeMessage) => {
|
|
78
|
+
const port = initializeMessage.initialize.port
|
|
79
|
+
// TODO handle error
|
|
80
|
+
const fakeSocket = {
|
|
81
|
+
send: port.postMessage.bind(port),
|
|
82
|
+
on(event, listener) {
|
|
83
|
+
switch (event) {
|
|
84
|
+
case 'close':
|
|
85
|
+
port.on('close', listener)
|
|
86
|
+
break
|
|
87
|
+
case 'message':
|
|
88
|
+
port.on('message', listener)
|
|
89
|
+
break
|
|
90
|
+
default:
|
|
91
|
+
console.warn('socket event not implemented', event, listener)
|
|
92
|
+
break
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
const handleOtherMessagesFromMessagePort = async (message) => {
|
|
97
|
+
// console.log('got port message', message)
|
|
98
|
+
if (message.result) {
|
|
99
|
+
Callback.resolve(message.id, message.result)
|
|
100
|
+
} else if (message.method) {
|
|
101
|
+
if (message.id) {
|
|
102
|
+
try {
|
|
103
|
+
const result = requiresSocket(message.method)
|
|
104
|
+
? await Command.invoke(
|
|
105
|
+
message.method,
|
|
106
|
+
fakeSocket,
|
|
107
|
+
...message.params
|
|
108
|
+
)
|
|
109
|
+
: await Command.invoke(message.method, ...message.params)
|
|
110
|
+
|
|
111
|
+
port.postMessage({
|
|
112
|
+
jsonrpc: '2.0',
|
|
113
|
+
id: message.id,
|
|
114
|
+
result: result ?? null,
|
|
115
|
+
})
|
|
116
|
+
} catch (error) {
|
|
117
|
+
// TODO duplicate code with Socket.js, clean this up
|
|
118
|
+
if (error instanceof Error.OperationalError) {
|
|
119
|
+
// console.log({ error })
|
|
120
|
+
const rawCause = error.cause()
|
|
121
|
+
const errorCause = rawCause ? rawCause.message : ''
|
|
122
|
+
const errorMessage = rawCause
|
|
123
|
+
? error.message.slice(0, error.message.indexOf(errorCause))
|
|
124
|
+
: ''
|
|
125
|
+
|
|
126
|
+
// console.info('expected error', error)
|
|
127
|
+
port.postMessage({
|
|
128
|
+
jsonrpc: '2.0',
|
|
129
|
+
id: message.id,
|
|
130
|
+
error: {
|
|
131
|
+
code: /* ExpectedError */ -32000,
|
|
132
|
+
message: error.message,
|
|
133
|
+
data: {
|
|
134
|
+
stack: error.originalStack,
|
|
135
|
+
codeFrame: error.originalCodeFrame,
|
|
136
|
+
category: error.category,
|
|
137
|
+
// @ts-ignore
|
|
138
|
+
stderr: error.stderr,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
})
|
|
142
|
+
} else {
|
|
143
|
+
console.error('[shared process] command failed to execute')
|
|
144
|
+
console.error(error)
|
|
145
|
+
// TODO check if socket is active
|
|
146
|
+
port.postMessage({
|
|
147
|
+
jsonrpc: '2.0',
|
|
148
|
+
id: message.id,
|
|
149
|
+
error: {
|
|
150
|
+
code: /* UnexpectedError */ -32001,
|
|
151
|
+
// @ts-ignore
|
|
152
|
+
message: error.toString(),
|
|
153
|
+
},
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
Command.execute(message.method, fakeSocket, ...message.params)
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
console.warn('unknown message', message)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
port.on('message', handleOtherMessagesFromMessagePort)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const handleMessageFromParentProcessElectron = async (message) => {
|
|
169
|
+
if (message.initialize) {
|
|
170
|
+
electronInitialize(message)
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
console.log({ message })
|
|
174
|
+
if ('result' in message) {
|
|
175
|
+
Callback.resolve(message.id, message.result)
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
console.log('unknown message from electron', message)
|
|
180
|
+
console.log({ message })
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// TODO maybe rename to hydrate
|
|
184
|
+
export const listen = () => {
|
|
185
|
+
// TODO tree-shake out if-else
|
|
186
|
+
// console.log({ ...process.env })
|
|
187
|
+
if (process.env.ELECTRON_RUN_AS_NODE && parentPort) {
|
|
188
|
+
// electron process listens to main process ipc
|
|
189
|
+
Debug.debug('is electron')
|
|
190
|
+
// @ts-ignore
|
|
191
|
+
parentPort.on('message', handleMessageFromParentProcessElectron)
|
|
192
|
+
electronSend('ready')
|
|
193
|
+
} else {
|
|
194
|
+
Debug.debug('is not electron')
|
|
195
|
+
// otherwise listen to web process ipc
|
|
196
|
+
// and when a socket is transferred,
|
|
197
|
+
// listen to socket messages also
|
|
198
|
+
process.on('message', handleMessageFromParentProcess)
|
|
199
|
+
if (process.send) {
|
|
200
|
+
process.send('ready')
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|