@lvce-editor/shared-process 0.0.26 → 0.0.29

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 (71) hide show
  1. package/config/builtinCommands.json +103 -87
  2. package/config/defaultKeyBindings.json +23 -18
  3. package/config/defaultSettings.json +3 -1
  4. package/index.js +2 -2
  5. package/package.json +13 -10
  6. package/src/parts/Assert/Assert.js +12 -5
  7. package/src/parts/ChildProcess/ChildProcessWithFork.js +19 -0
  8. package/src/parts/ClipBoard/ClipBoard.ipc.js +3 -4
  9. package/src/parts/Command/Command.js +30 -17
  10. package/src/parts/Credentials/Credentials.ipc.js +6 -7
  11. package/src/parts/Developer/Developer.ipc.js +10 -17
  12. package/src/parts/Electron/Electron.ipc.js +15 -16
  13. package/src/parts/Electron/Electron.js +43 -34
  14. package/src/parts/ExtensionHost/ExtensionHost.ipc.js +15 -72
  15. package/src/parts/ExtensionHost/ExtensionHost.js +59 -138
  16. package/src/parts/ExtensionHostIpc/ExtensionHostIpc.js +5 -0
  17. package/src/parts/ExtensionHostIpc/ExtensionHostIpcWithChildProcess.js +57 -0
  18. package/src/parts/ExtensionHostIpc/ExtensionHostIpcWithWorker.js +38 -0
  19. package/src/parts/ExtensionHostRpc/ExtensionHostRpc.js +82 -0
  20. package/src/parts/ExtensionManagement/ExtensionManagement.ipc.js +16 -8
  21. package/src/parts/ExtensionManagement/ExtensionManagement.js +5 -2
  22. package/src/parts/{ExtensionHost/ExtensionHostColorTheme.js → ExtensionManagement/ExtensionManagementColorTheme.js} +1 -1
  23. package/src/parts/{ExtensionHost/ExtensionHostIconTheme.js → ExtensionManagement/ExtensionManagementIconTheme.js} +1 -1
  24. package/src/parts/{ExtensionHost/ExtensionHostLanguages.js → ExtensionManagement/ExtensionManagementLanguages.js} +1 -1
  25. package/src/parts/FileSystem/FileSystem.ipc.js +14 -15
  26. package/src/parts/FileSystem/FileSystem.js +56 -1
  27. package/src/parts/JsonRpc/JsonRpc.js +2 -2
  28. package/src/parts/Native/Native.ipc.js +2 -3
  29. package/src/parts/OutputChannel/OutputChannel.ipc.js +16 -19
  30. package/src/parts/ParentIpc/ParentIpc.js +93 -44
  31. package/src/parts/Platform/Platform.ipc.js +15 -17
  32. package/src/parts/Platform/Platform.js +1 -1
  33. package/src/parts/Preferences/Preferences.ipc.js +2 -3
  34. package/src/parts/Preferences/Preferences.js +0 -2
  35. package/src/parts/PrettyError/PrettyError.js +5 -2
  36. package/src/parts/Process/Process.ipc.js +3 -4
  37. package/src/parts/RequiresSocket/RequiresSocket.js +6 -5
  38. package/src/parts/Search/Search.ipc.js +2 -3
  39. package/src/parts/SearchFile/SearchFile.ipc.js +2 -3
  40. package/src/parts/Stats/Stats.js +8 -7
  41. package/src/parts/Terminal/Terminal.ipc.js +9 -9
  42. package/src/parts/Terminal/Terminal.js +1 -1
  43. package/src/parts/TextDocument/TextDocument.ipc.js +2 -3
  44. package/src/parts/Timeout/Timeout.js +7 -0
  45. package/src/parts/WebSocketServer/WebSocketServer.ipc.js +2 -3
  46. package/src/parts/Workspace/Workspace.ipc.js +3 -4
  47. package/src/sharedProcessMain.js +13 -4
  48. package/src/parts/Extension/Extension.js +0 -7
  49. package/src/parts/ExtensionHost/ExtensionHostBraceCompletion.js +0 -14
  50. package/src/parts/ExtensionHost/ExtensionHostClosingTag.js +0 -14
  51. package/src/parts/ExtensionHost/ExtensionHostCommand.js +0 -19
  52. package/src/parts/ExtensionHost/ExtensionHostCompletion.js +0 -12
  53. package/src/parts/ExtensionHost/ExtensionHostDefinition.js +0 -12
  54. package/src/parts/ExtensionHost/ExtensionHostDiagnostic.js +0 -7
  55. package/src/parts/ExtensionHost/ExtensionHostFileSystem.js +0 -23
  56. package/src/parts/ExtensionHost/ExtensionHostFormatting.js +0 -4
  57. package/src/parts/ExtensionHost/ExtensionHostHover.js +0 -12
  58. package/src/parts/ExtensionHost/ExtensionHostImplementation.js +0 -12
  59. package/src/parts/ExtensionHost/ExtensionHostManagement.js +0 -26
  60. package/src/parts/ExtensionHost/ExtensionHostOutput.js +0 -3
  61. package/src/parts/ExtensionHost/ExtensionHostQuickPick.js +0 -6
  62. package/src/parts/ExtensionHost/ExtensionHostReference.js +0 -12
  63. package/src/parts/ExtensionHost/ExtensionHostRename.js +0 -27
  64. package/src/parts/ExtensionHost/ExtensionHostSemanticTokens.js +0 -10
  65. package/src/parts/ExtensionHost/ExtensionHostSourceControl.js +0 -15
  66. package/src/parts/ExtensionHost/ExtensionHostStats.js +0 -3
  67. package/src/parts/ExtensionHost/ExtensionHostStatusBar.js +0 -7
  68. package/src/parts/ExtensionHost/ExtensionHostTabCompletion.js +0 -12
  69. package/src/parts/ExtensionHost/ExtensionHostTextDocument.js +0 -35
  70. package/src/parts/ExtensionHost/ExtensionHostTypeDefinition.js +0 -12
  71. package/src/parts/ExtensionHost/ExtensionHostWorkspace.js +0 -3
@@ -1,163 +1,84 @@
1
1
  import VError from 'verror'
2
+ import * as Assert from '../Assert/Assert.js'
2
3
  import * as Callback from '../Callback/Callback.js'
3
- import * as ChildProcess from '../ChildProcess/ChildProcess.js'
4
- import * as Error from '../Error/Error.js'
4
+ import * as ExtensionHostIpc from '../ExtensionHostIpc/ExtensionHostIpc.js'
5
+ import * as ExtensionHostRpc from '../ExtensionHostRpc/ExtensionHostRpc.js'
5
6
  import * as JsonRpc from '../JsonRpc/JsonRpc.js'
6
- import * as Platform from '../Platform/Platform.js'
7
- import * as Socket from '../Socket/Socket.js'
8
- import * as Assert from '../Assert/Assert.js'
9
- // TODO maybe rename to extension host management for clarity
10
7
 
11
- // TODO needed? probably not
12
- const NULL_EXTENSION_HOST = {
13
- dispose() {
14
- throw new VError('not implemented')
15
- },
16
- send(message) {
17
- throw new VError(
18
- 'extension host must be started before commands can be invoked'
19
- )
20
- },
21
- async invoke(commandId, ...args) {
22
- throw new VError(
23
- 'extension host must be started before commands can be invoked'
24
- )
25
- },
26
- }
8
+ // TODO maybe rename to extension host management for clarity
27
9
 
28
10
  /**
29
- * @type{{
30
- * extensionHost: {dispose:()=>void, invoke:(commandId, ...args)=>Promise<any>},
31
- * }}
11
+ * @type{{extensionHosts:any }}
32
12
  */
33
13
  export const state = {
34
- extensionHost: NULL_EXTENSION_HOST,
14
+ extensionHosts: Object.create(null),
35
15
  }
36
16
 
37
- const onMessage = (message) => {
38
- if (message.id) {
39
- if ('result' in message) {
40
- Callback.resolve(message.id, message.result)
41
- } else if ('error' in message) {
42
- Callback.reject(
43
- message.id,
44
- new Error.OperationalError({
45
- code: 'E_CALLBACK_REJECTED',
46
- message: message.error.message,
47
- stack: message.error.data.stack,
48
- codeFrame: message.error.data.codeFrame,
49
- // @ts-ignore
50
- stderr: message.error.data.stderr,
51
- })
52
- )
53
- } else {
54
- Socket.send(message)
55
- }
56
- } else {
57
- Socket.send(message)
58
- }
17
+ // TODO this function is very ugly and has probably memory leaks
18
+ const createExtensionHost = async (socket) => {
19
+ Assert.object(socket)
20
+ const ipc = ExtensionHostIpc.create()
21
+ const rpc = ExtensionHostRpc.create(ipc, socket)
22
+ return rpc
59
23
  }
60
24
 
61
- // TODO this function is very ugly and has probably memory leaks
62
25
  export const start = async (socket) => {
63
26
  try {
64
- Assert.object(socket)
65
- const extensionHostPath = Platform.getExtensionHostPath()
66
- const childProcess = ChildProcess.fork(extensionHostPath, {
67
- execArgv: [
68
- '--experimental-json-modules',
69
- '--max-old-space-size=60',
70
- '--enable-source-maps',
71
- ],
72
- env: {
73
- ...process.env,
74
- LOGS_DIR: Platform.getLogsDir(),
75
- CONFIG_DIR: Platform.getConfigDir(),
76
- },
77
- })
78
-
79
- const handleChildProcessError = (error) => {
80
- console.error(`[Extension Host] ${error}`)
81
- }
82
-
83
- const handleChildProcessExit = (exitCode) => {
84
- console.info(
85
- `[SharedProcess] Extension Host exited with code ${exitCode}`
86
- )
87
- state.extensionHost = NULL_EXTENSION_HOST
88
- }
89
-
90
- const handleSocketClose = () => {
91
- console.info('[shared process] disposing extension host')
92
- childProcess.kill()
93
- }
94
-
95
- socket.on('close', handleSocketClose)
96
-
97
- childProcess.on('error', handleChildProcessError)
98
-
99
- childProcess.on('exit', handleChildProcessExit)
100
-
101
- state.extensionHost = {
102
- invoke(...args) {
103
- return JsonRpc.invoke(childProcess, ...args)
104
- },
105
- dispose() {
106
- childProcess.kill()
107
- },
108
- }
109
-
110
- await new Promise((resolve, reject) => {
111
- const handleFirstError = (error) => {
112
- cleanup()
113
- reject(error)
114
- }
115
- const handleFirstExit = (exitCode) => {
116
- cleanup()
117
- reject(
118
- new globalThis.Error(`Extension Host exited with code ${exitCode}`)
119
- )
27
+ const id = 1
28
+ console.log('start extension host', id)
29
+ const handleMessage = (message) => {
30
+ if ('result' in message) {
31
+ Callback.resolve(message.id, message.result)
32
+ } else if ('error' in message) {
33
+ Callback.reject(message.id, message.error)
34
+ } else {
35
+ console.error('unsupported message type')
120
36
  }
121
- const handleFirstMessage = () => {
122
- cleanup()
123
- childProcess.on('message', onMessage)
124
- resolve(undefined)
125
- }
126
- const handleSocketClose = () => {
127
- cleanup()
128
- childProcess.off('error', handleChildProcessError)
129
- childProcess.off('exit', handleChildProcessExit)
130
- resolve(undefined)
131
- }
132
- const cleanup = () => {
133
- childProcess.off('error', handleFirstError)
134
- childProcess.off('exit', handleFirstExit)
135
- childProcess.off('message', handleFirstMessage)
136
- }
137
- childProcess.on('error', handleFirstError)
138
- childProcess.on('exit', handleFirstExit)
139
- childProcess.on('message', handleFirstMessage)
140
- socket.on('close', handleSocketClose)
141
- })
37
+ }
38
+ const extensionHost = await createExtensionHost(socket)
39
+ extensionHost.on('message', handleMessage)
40
+ state.extensionHosts[id] = extensionHost
41
+ return id
142
42
  } catch (error) {
143
43
  // @ts-ignore
144
44
  throw new VError(error, 'Failed to start extension host')
145
45
  }
146
46
  }
147
47
 
148
- export const stop = () => {
149
- state.extensionHost.dispose()
150
- state.extensionHost = NULL_EXTENSION_HOST
151
- }
152
-
153
- export const restart = () => {
154
- stop()
155
- start()
48
+ export const send = async (socket, id, message) => {
49
+ Assert.number(id)
50
+ Assert.object(message)
51
+ console.log(Object.keys(state))
52
+ const extensionHost = state.extensionHosts[id]
53
+ if (!extensionHost) {
54
+ throw new VError(`no extension host with id ${id} found`)
55
+ }
56
+ try {
57
+ const result = await JsonRpc.invoke(
58
+ extensionHost,
59
+ message.method,
60
+ ...message.params
61
+ )
62
+ socket.send({
63
+ jsonrpc: '2.0',
64
+ id: message.id,
65
+ result,
66
+ })
67
+ } catch (error) {
68
+ // console.log({ error })
69
+ socket.send({
70
+ jsonrpc: '2.0',
71
+ id: message.id,
72
+ error,
73
+ })
74
+ }
156
75
  }
157
76
 
158
- export const wrapExtensionHostCommand = (fn) => {
159
- const wrappedExtensionHostCommand = (...args) => {
160
- return fn(state.extensionHost, ...args)
77
+ export const dispose = (id) => {
78
+ const extensionHost = state.extensionHosts[id]
79
+ if (!extensionHost) {
80
+ throw new VError(`no extension host with id ${id} found`)
161
81
  }
162
- return wrappedExtensionHostCommand
82
+ extensionHost.dispose()
83
+ delete state.extensionHosts[id]
163
84
  }
@@ -0,0 +1,5 @@
1
+ import * as ExtensionHostIpcWithChildProcess from './ExtensionHostIpcWithChildProcess.js'
2
+
3
+ export const create = () => {
4
+ return ExtensionHostIpcWithChildProcess.create()
5
+ }
@@ -0,0 +1,57 @@
1
+ import * as ChildProcess from '../ChildProcess/ChildProcess.js'
2
+ import * as Platform from '../Platform/Platform.js'
3
+
4
+ export const create = () => {
5
+ const extensionHostPath = Platform.getExtensionHostPath()
6
+ const childProcess = ChildProcess.fork(extensionHostPath, {
7
+ execArgv: [
8
+ '--experimental-json-modules',
9
+ '--max-old-space-size=60',
10
+ '--enable-source-maps',
11
+ ],
12
+ env: {
13
+ ...process.env,
14
+ LOGS_DIR: Platform.getLogsDir(),
15
+ CONFIG_DIR: Platform.getConfigDir(),
16
+ },
17
+ })
18
+ return {
19
+ on(event, listener) {
20
+ switch (event) {
21
+ case 'message':
22
+ childProcess.on('message', listener)
23
+ break
24
+ case 'error':
25
+ childProcess.on('error', listener)
26
+ break
27
+ case 'exit':
28
+ childProcess.on('exit', listener)
29
+ break
30
+ default:
31
+ throw new Error(`unsupported event type ${event}`)
32
+ }
33
+ },
34
+ off(event, listener) {
35
+ switch (event) {
36
+ case 'message':
37
+ childProcess.off('message', listener)
38
+ break
39
+ case 'error':
40
+ childProcess.off('error', listener)
41
+ break
42
+ case 'exit':
43
+ childProcess.off('exit', listener)
44
+ break
45
+ default:
46
+ throw new Error(`unsupported event type ${event}`)
47
+ }
48
+ },
49
+ send(message) {
50
+ childProcess.send(message)
51
+ },
52
+ dispose() {
53
+ childProcess.kill()
54
+ },
55
+ _process: childProcess,
56
+ }
57
+ }
@@ -0,0 +1,38 @@
1
+ import { Worker } from 'node:worker_threads'
2
+ import * as Platform from '../Platform/Platform.js'
3
+
4
+ export const create = () => {
5
+ const extensionHostPath = Platform.getExtensionHostPath()
6
+ const child = new Worker(extensionHostPath, {
7
+ execArgv: [
8
+ '--experimental-json-modules',
9
+ '--max-old-space-size=60',
10
+ '--enable-source-maps',
11
+ ],
12
+ env: {
13
+ ...process.env,
14
+ LOGS_DIR: Platform.getLogsDir(),
15
+ CONFIG_DIR: Platform.getConfigDir(),
16
+ },
17
+ })
18
+ return {
19
+ on(event, listener) {
20
+ switch (event) {
21
+ case 'message':
22
+ child.on('message', listener)
23
+ break
24
+ case 'error':
25
+ child.on('error', listener)
26
+ break
27
+ default:
28
+ break
29
+ }
30
+ },
31
+ send(message) {
32
+ child.postMessage(message)
33
+ },
34
+ dispose() {
35
+ child.terminate()
36
+ },
37
+ }
38
+ }
@@ -0,0 +1,82 @@
1
+ import VError from 'verror'
2
+ import * as Assert from '../Assert/Assert.js'
3
+ import * as Timeout from '../Timeout/Timeout.js'
4
+
5
+ // TODO maybe rename to extension host management for clarity
6
+
7
+ const CONNECTION_TIMEOUT = 1000
8
+
9
+ export const create = async (ipc, socket) => {
10
+ Assert.object(socket)
11
+
12
+ const handleChildProcessError = (error) => {
13
+ console.error(`[Extension Host] ${error}`)
14
+ }
15
+
16
+ const handleChildProcessExit = (exitCode) => {
17
+ console.info(`[SharedProcess] Extension Host exited with code ${exitCode}`)
18
+ }
19
+
20
+ const handleSocketClose = () => {
21
+ console.info('[shared process] disposing extension host')
22
+ ipc.dispose()
23
+ }
24
+
25
+ socket.on('close', handleSocketClose)
26
+
27
+ ipc.on('error', handleChildProcessError)
28
+
29
+ ipc.on('exit', handleChildProcessExit)
30
+
31
+ await new Promise((resolve, reject) => {
32
+ const handleFirstError = (error) => {
33
+ cleanup()
34
+ reject(error)
35
+ }
36
+ const handleFirstExit = (exitCode) => {
37
+ cleanup()
38
+ reject(new VError(`Extension Host exited with code ${exitCode}`))
39
+ }
40
+ const handleFirstMessage = (message) => {
41
+ cleanup()
42
+ if (message === 'ready') {
43
+ resolve(undefined)
44
+ } else {
45
+ reject(new VError('Unexpected first message from extension host'))
46
+ }
47
+ }
48
+ const handleSocketClose = () => {
49
+ cleanup()
50
+ ipc.off('error', handleChildProcessError)
51
+ ipc.off('exit', handleChildProcessExit)
52
+ resolve(undefined)
53
+ }
54
+ const cleanup = () => {
55
+ ipc.off('error', handleFirstError)
56
+ ipc.off('exit', handleFirstExit)
57
+ ipc.off('message', handleFirstMessage)
58
+ clearTimeout(timeout)
59
+ }
60
+ const handleTimeout = () => {
61
+ cleanup()
62
+ reject(new VError('Extension host did not connect'))
63
+ }
64
+ const timeout = Timeout.setTimeout(handleTimeout, CONNECTION_TIMEOUT)
65
+ ipc.on('error', handleFirstError)
66
+ ipc.on('exit', handleFirstExit)
67
+ ipc.on('message', handleFirstMessage)
68
+ socket.on('close', handleSocketClose)
69
+ })
70
+
71
+ return {
72
+ on(event, listener) {
73
+ ipc.on(event, listener)
74
+ },
75
+ send(message) {
76
+ ipc.send(message)
77
+ },
78
+ dispose() {
79
+ ipc.dispose()
80
+ },
81
+ }
82
+ }
@@ -1,11 +1,19 @@
1
- import * as Command from '../Command/Command.js'
1
+ import * as ExtensionHostIconTheme from '../ExtensionManagement/ExtensionManagementIconTheme.js'
2
2
  import * as ExtensionManagement from './ExtensionManagement.js'
3
+ import * as ExtensionHostColorTheme from './ExtensionManagementColorTheme.js'
4
+ import * as ExtensionHostLanguages from './ExtensionManagementLanguages.js'
3
5
 
4
- export const __initialize__ = () => {
5
- Command.register('ExtensionManagement.install', ExtensionManagement.install)
6
- Command.register('ExtensionManagement.uninstall', ExtensionManagement.uninstall)
7
- Command.register('ExtensionManagement.enable', ExtensionManagement.enable)
8
- Command.register('ExtensionManagement.disable', ExtensionManagement.disable)
9
- Command.register('ExtensionManagement.getAllExtensions', ExtensionManagement.getAllExtensions)
10
- Command.register('ExtensionManagement.getExtensions', ExtensionManagement.getExtensions)
6
+ // prettier-ignore
7
+ export const Commands = {
8
+ 'ExtensionManagement.install': ExtensionManagement.install,
9
+ 'ExtensionManagement.uninstall': ExtensionManagement.uninstall,
10
+ 'ExtensionManagement.enable': ExtensionManagement.enable,
11
+ 'ExtensionManagement.disable': ExtensionManagement.disable,
12
+ 'ExtensionManagement.getAllExtensions': ExtensionManagement.getAllExtensions,
13
+ 'ExtensionManagement.getExtensions': ExtensionManagement.getExtensions,
14
+ 'ExtensionHost.getColorThemeJson': ExtensionHostColorTheme.getColorThemeJson,
15
+ 'ExtensionHost.getColorThemeNames': ExtensionHostColorTheme.getColorThemeNames,
16
+ 'ExtensionHost.getLanguages': ExtensionHostLanguages.getLanguages,
17
+ 'ExtensionHost.getLanguageConfiguration': ExtensionHostLanguages.getLanguageConfiguration,
18
+ 'ExtensionHost.getIconThemeJson': ExtensionHostIconTheme.getIconTheme,
11
19
  }
@@ -1,5 +1,6 @@
1
1
  import { mkdir, readdir, rename, rm } from 'node:fs/promises'
2
2
  import { performance } from 'node:perf_hooks'
3
+ import path from 'node:path'
3
4
  import isObject from 'is-object'
4
5
  import VError from 'verror'
5
6
  import * as Debug from '../Debug/Debug.js'
@@ -8,10 +9,12 @@ import * as ReadJson from '../JsonFile/JsonFile.js'
8
9
  import * as Path from '../Path/Path.js'
9
10
  import * as Platform from '../Platform/Platform.js'
10
11
  import * as Queue from '../Queue/Queue.js'
11
- import path from 'node:path'
12
12
 
13
13
  const isLanguageBasics = (extension) => {
14
- return extension.id.includes('language-basics')
14
+ if (extension && extension.id) {
15
+ return extension.id.includes('language-basics')
16
+ }
17
+ return false
15
18
  }
16
19
 
17
20
  export const state = {
@@ -1,6 +1,6 @@
1
1
  import * as Error from '../Error/Error.js'
2
- import * as ExtensionManagement from '../ExtensionManagement/ExtensionManagement.js'
3
2
  import * as ReadJson from '../JsonFile/JsonFile.js'
3
+ import * as ExtensionManagement from './ExtensionManagement.js'
4
4
 
5
5
  // TODO test this function
6
6
  // TODO very similar with getIconTheme
@@ -1,6 +1,6 @@
1
1
  import * as Error from '../Error/Error.js'
2
- import * as ExtensionManagement from '../ExtensionManagement/ExtensionManagement.js'
3
2
  import * as ReadJson from '../JsonFile/JsonFile.js'
3
+ import * as ExtensionManagement from './ExtensionManagement.js'
4
4
 
5
5
  export const getIconTheme = async (iconThemeId) => {
6
6
  const extensions = await ExtensionManagement.getExtensions()
@@ -2,9 +2,9 @@ import { join } from 'node:path'
2
2
  import { pathToFileURL } from 'node:url'
3
3
  import VError from 'verror'
4
4
  import * as Error from '../Error/Error.js'
5
- import * as ExtensionManagement from '../ExtensionManagement/ExtensionManagement.js'
6
5
  import * as ReadJson from '../JsonFile/JsonFile.js'
7
6
  import * as Path from '../Path/Path.js'
7
+ import * as ExtensionManagement from './ExtensionManagement.js'
8
8
 
9
9
  const getExtensionLanguages = (extension) => {
10
10
  if (!extension.languages) {
@@ -1,4 +1,3 @@
1
- import * as Command from '../Command/Command.js'
2
1
  import * as FileSystem from './FileSystem.js'
3
2
 
4
3
  // TODO ugly code here -> should be inside FileSystem.js
@@ -6,7 +5,6 @@ const fileSystemWatch = (socket, id, path) => {
6
5
  // const watcher = FileSystem.watch(path, {
7
6
  // onAll(...args) {
8
7
  // socket.send(JSON.stringify([/* callback */ id, /* event */ args]))
9
-
10
8
  // }
11
9
  // }
12
10
  // )
@@ -16,17 +14,18 @@ const fileSystemWatch = (socket, id, path) => {
16
14
  }
17
15
 
18
16
  // TODO separate ipc from code like in renderer worker
19
- export const __initialize__ = () => {
20
- Command.register('FileSystem.readFile', FileSystem.readFile)
21
- Command.register('FileSystem.writeFile', FileSystem.writeFile)
22
- Command.register('FileSystem.readDirWithFileTypes', FileSystem.readDirWithFileTypes)
23
- Command.register('FileSystem.remove', FileSystem.remove)
24
- Command.register('FileSystem.mkdir', FileSystem.mkdir)
25
- // Command.register(106, fileSystemWatch) // TODO
26
- Command.register('FileSystem.createFile', FileSystem.createFile)
27
- Command.register('FileSystem.createFolder', FileSystem.createFolder)
28
- Command.register('FileSystem.rename', FileSystem.rename)
29
- Command.register('FileSystem.ensureFile', FileSystem.ensureFile)
30
- Command.register('FileSystem.copy', FileSystem.copy)
31
- Command.register('FileSystem.getPathSeparator', FileSystem.getPathSeparator)
17
+ export const Commands = {
18
+ 'FileSystem.copy': FileSystem.copy,
19
+ 'FileSystem.createFile': FileSystem.createFile,
20
+ 'FileSystem.createFolder': FileSystem.createFolder,
21
+ 'FileSystem.ensureFile': FileSystem.ensureFile,
22
+ 'FileSystem.getPathSeparator': FileSystem.getPathSeparator,
23
+ 'FileSystem.getRealPath': FileSystem.getRealPath,
24
+ 'FileSystem.mkdir': FileSystem.mkdir,
25
+ 'FileSystem.readDirWithFileTypes': FileSystem.readDirWithFileTypes,
26
+ 'FileSystem.readFile': FileSystem.readFile,
27
+ 'FileSystem.remove': FileSystem.remove,
28
+ 'FileSystem.rename': FileSystem.rename,
29
+ 'FileSystem.stat': FileSystem.stat,
30
+ 'FileSystem.writeFile': FileSystem.writeFile,
32
31
  }
@@ -6,6 +6,7 @@ import * as Error from '../Error/Error.js'
6
6
  import * as Path from '../Path/Path.js'
7
7
  import * as Trash from '../Trash/Trash.js'
8
8
  import * as Platform from '../Platform/Platform.js'
9
+ import VError from 'verror'
9
10
 
10
11
  export const state = {
11
12
  watcherMap: Object.create(null),
@@ -15,6 +16,17 @@ export const copy = async (source, target) => {
15
16
  try {
16
17
  await fs.cp(source, target, { recursive: true })
17
18
  } catch (error) {
19
+ if (
20
+ error &&
21
+ error.message &&
22
+ error.message.startsWith(
23
+ 'Invalid src or dest: cp returned EINVAL (src and dest cannot be the same)'
24
+ )
25
+ ) {
26
+ throw new VError(
27
+ `Failed to copy "${source}" to "${target}": src and dest cannot be the same`
28
+ )
29
+ }
18
30
  throw new Error.OperationalError({
19
31
  cause: error,
20
32
  code: 'E_FILE_SYSTEM_ERROR',
@@ -26,7 +38,7 @@ export const copy = async (source, target) => {
26
38
 
27
39
  export const readFile = async (path) => {
28
40
  try {
29
- const content = await fs.readFile(path, 'utf-8')
41
+ const content = await fs.readFile(path, 'utf8')
30
42
  return content
31
43
  } catch (error) {
32
44
  throw new Error.OperationalError({
@@ -135,6 +147,9 @@ export const exists = async (path) => {
135
147
  }
136
148
  }
137
149
 
150
+ /**
151
+ * @param {import('fs').Dirent|import('fs').StatsBase} dirent
152
+ */
138
153
  const getType = (dirent) => {
139
154
  if (dirent.isFile()) {
140
155
  return 'file'
@@ -142,9 +157,24 @@ const getType = (dirent) => {
142
157
  if (dirent.isDirectory()) {
143
158
  return 'directory'
144
159
  }
160
+ if (dirent.isSymbolicLink()) {
161
+ return 'symlink'
162
+ }
163
+ if (dirent.isSocket()) {
164
+ return 'socket'
165
+ }
166
+ if (dirent.isBlockDevice()) {
167
+ return 'block-device'
168
+ }
169
+ if (dirent.isCharacterDevice()) {
170
+ return 'character-device'
171
+ }
145
172
  return 'unknown'
146
173
  }
147
174
 
175
+ /**
176
+ * @param {import('fs').Dirent} dirent
177
+ */
148
178
  const toPrettyDirent = (dirent) => {
149
179
  return {
150
180
  name: dirent.name,
@@ -212,6 +242,31 @@ export const getPathSeparator = () => {
212
242
  return Platform.getPathSeparator()
213
243
  }
214
244
 
245
+ export const getRealPath = async (path) => {
246
+ try {
247
+ return await fs.realpath(path)
248
+ } catch (error) {
249
+ // @ts-ignore
250
+ if (error && error instanceof globalThis.Error && error.code === 'ENOENT') {
251
+ let content
252
+ try {
253
+ content = await fs.readlink(path)
254
+ } catch {
255
+ throw new VError(error, `Failed to resolve real path for ${path}`)
256
+ }
257
+ throw new VError(`Broken symbolic link: File not found ${content}`)
258
+ }
259
+ throw new VError(error, `Failed to resolve real path for ${path}`)
260
+ }
261
+ }
262
+
263
+ export const stat = async (path) => {
264
+ const stats = await fs.stat(path)
265
+ const type = getType(stats)
266
+ return type
267
+ }
268
+
269
+ // getRealPath('/home/simon/Documents/levivilet/lvce-editor/node_modules/.bin/abc') //?
215
270
  // export const unwatch = (id) => {
216
271
  // state.watchers[id].close()
217
272
  // delete state.watchers[id]
@@ -8,11 +8,11 @@ export const send = (transport, method, ...params) => {
8
8
  })
9
9
  }
10
10
 
11
- export const invoke = (transport, method, ...params) => {
11
+ export const invoke = (ipc, method, ...params) => {
12
12
  return new Promise((resolve, reject) => {
13
13
  // TODO use one map instead of two
14
14
  const callbackId = Callback.register(resolve, reject)
15
- transport.send({
15
+ ipc.send({
16
16
  jsonrpc: '2.0',
17
17
  method,
18
18
  params,
@@ -1,6 +1,5 @@
1
- import * as Command from '../Command/Command.js'
2
1
  import * as Native from './Native.js'
3
2
 
4
- export const __initialize__ = () => {
5
- Command.register('Native.openFolder', Native.openFolder)
3
+ export const Commands = {
4
+ 'Native.openFolder': Native.openFolder,
6
5
  }