@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.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +41 -0
  3. package/bin/sharedProcess.js +3 -0
  4. package/index.js +6 -0
  5. package/package.json +96 -0
  6. package/src/parts/Assert/Assert.js +57 -0
  7. package/src/parts/Callback/Callback.js +48 -0
  8. package/src/parts/ChildProcess/ChildProcess.js +98 -0
  9. package/src/parts/ClipBoard/ClipBoard.ipc.js +7 -0
  10. package/src/parts/ClipBoard/ClipBoard.js +23 -0
  11. package/src/parts/ClipBoard/ClipBoardGnome.js +78 -0
  12. package/src/parts/ClipBoard/ClipBoardNoop.js +14 -0
  13. package/src/parts/ClipBoard/ClipBoardWindows.js +22 -0
  14. package/src/parts/Command/Command.js +264 -0
  15. package/src/parts/Configuration/Configuration.ipc.js +7 -0
  16. package/src/parts/Configuration/Configuration.js +39 -0
  17. package/src/parts/Credentials/Credentials.ipc.js +10 -0
  18. package/src/parts/Credentials/Credentials.js +69 -0
  19. package/src/parts/Debug/Debug.js +3 -0
  20. package/src/parts/Developer/Developer.ipc.js +20 -0
  21. package/src/parts/Developer/Developer.js +132 -0
  22. package/src/parts/Download/Download.js +33 -0
  23. package/src/parts/Electron/Electron.ipc.js +19 -0
  24. package/src/parts/Electron/Electron.js +99 -0
  25. package/src/parts/Env/Env.js +3 -0
  26. package/src/parts/Error/Error.js +30 -0
  27. package/src/parts/ErrorHandling/ErrorHandling.js +40 -0
  28. package/src/parts/Exec/Exec.js +16 -0
  29. package/src/parts/Extension/Extension.js +7 -0
  30. package/src/parts/ExtensionHost/ExtensionHost.ipc.js +80 -0
  31. package/src/parts/ExtensionHost/ExtensionHost.js +163 -0
  32. package/src/parts/ExtensionHost/ExtensionHostBraceCompletion.js +14 -0
  33. package/src/parts/ExtensionHost/ExtensionHostClosingTag.js +14 -0
  34. package/src/parts/ExtensionHost/ExtensionHostColorTheme.js +60 -0
  35. package/src/parts/ExtensionHost/ExtensionHostCommand.js +19 -0
  36. package/src/parts/ExtensionHost/ExtensionHostCompletion.js +12 -0
  37. package/src/parts/ExtensionHost/ExtensionHostDefinition.js +12 -0
  38. package/src/parts/ExtensionHost/ExtensionHostDiagnostic.js +7 -0
  39. package/src/parts/ExtensionHost/ExtensionHostFileSystem.js +23 -0
  40. package/src/parts/ExtensionHost/ExtensionHostFormatting.js +4 -0
  41. package/src/parts/ExtensionHost/ExtensionHostHover.js +12 -0
  42. package/src/parts/ExtensionHost/ExtensionHostIconTheme.js +35 -0
  43. package/src/parts/ExtensionHost/ExtensionHostImplementation.js +12 -0
  44. package/src/parts/ExtensionHost/ExtensionHostKeyBindings.js +23 -0
  45. package/src/parts/ExtensionHost/ExtensionHostLanguages.js +84 -0
  46. package/src/parts/ExtensionHost/ExtensionHostManagement.js +26 -0
  47. package/src/parts/ExtensionHost/ExtensionHostOutput.js +3 -0
  48. package/src/parts/ExtensionHost/ExtensionHostQuickPick.js +6 -0
  49. package/src/parts/ExtensionHost/ExtensionHostReference.js +12 -0
  50. package/src/parts/ExtensionHost/ExtensionHostRename.js +27 -0
  51. package/src/parts/ExtensionHost/ExtensionHostSemanticTokens.js +10 -0
  52. package/src/parts/ExtensionHost/ExtensionHostSourceControl.js +15 -0
  53. package/src/parts/ExtensionHost/ExtensionHostStats.js +3 -0
  54. package/src/parts/ExtensionHost/ExtensionHostStatusBar.js +7 -0
  55. package/src/parts/ExtensionHost/ExtensionHostTabCompletion.js +12 -0
  56. package/src/parts/ExtensionHost/ExtensionHostTextDocument.js +35 -0
  57. package/src/parts/ExtensionHost/ExtensionHostTypeDefinition.js +12 -0
  58. package/src/parts/ExtensionHost/ExtensionHostWorkspace.js +3 -0
  59. package/src/parts/ExtensionManagement/ExtensionManagement.ipc.js +11 -0
  60. package/src/parts/ExtensionManagement/ExtensionManagement.js +319 -0
  61. package/src/parts/FileSystem/FileSystem.ipc.js +32 -0
  62. package/src/parts/FileSystem/FileSystem.js +228 -0
  63. package/src/parts/Git/Git.js +58 -0
  64. package/src/parts/Json/Json.js +67 -0
  65. package/src/parts/JsonFile/JsonFile.js +14 -0
  66. package/src/parts/JsonRpc/JsonRpc.js +22 -0
  67. package/src/parts/Native/Native.ipc.js +6 -0
  68. package/src/parts/Native/Native.js +14 -0
  69. package/src/parts/OutputChannel/OutputChannel.ipc.js +51 -0
  70. package/src/parts/OutputChannel/OutputChannel.js +37 -0
  71. package/src/parts/ParentIpc/ParentIpc.js +203 -0
  72. package/src/parts/Path/Path.js +17 -0
  73. package/src/parts/Platform/Platform.ipc.js +18 -0
  74. package/src/parts/Platform/Platform.js +165 -0
  75. package/src/parts/Preferences/Preferences.ipc.js +6 -0
  76. package/src/parts/Preferences/Preferences.js +75 -0
  77. package/src/parts/PrettyError/PrettyError.js +86 -0
  78. package/src/parts/Process/Process.ipc.js +7 -0
  79. package/src/parts/Process/Process.js +7 -0
  80. package/src/parts/Queue/Queue.js +35 -0
  81. package/src/parts/RequiresSocket/RequiresSocket.js +11 -0
  82. package/src/parts/RgPath/RgPath.js +1 -0
  83. package/src/parts/Root/Root.js +6 -0
  84. package/src/parts/Search/Search.ipc.js +6 -0
  85. package/src/parts/Search/Search.js +101 -0
  86. package/src/parts/SearchFile/SearchFile.ipc.js +6 -0
  87. package/src/parts/SearchFile/SearchFile.js +51 -0
  88. package/src/parts/Socket/Socket.js +132 -0
  89. package/src/parts/Stats/Stats.js +45 -0
  90. package/src/parts/Terminal/Terminal.ipc.js +45 -0
  91. package/src/parts/Terminal/Terminal.js +161 -0
  92. package/src/parts/TextDocument/TextDocument.ipc.js +6 -0
  93. package/src/parts/TextDocument/TextDocument.js +37 -0
  94. package/src/parts/Trace/Trace.js +15 -0
  95. package/src/parts/Trash/Trash.js +11 -0
  96. package/src/parts/WebSocketServer/WebSocketServer.ipc.js +6 -0
  97. package/src/parts/WebSocketServer/WebSocketServer.js +43 -0
  98. package/src/parts/Workspace/Workspace.ipc.js +7 -0
  99. package/src/parts/Workspace/Workspace.js +107 -0
  100. package/src/sharedProcessMain.js +61 -0
  101. package/test/Callback.test.js +42 -0
  102. package/test/ClipBoard.test.js +105 -0
  103. package/test/Credentials.test.js +147 -0
  104. package/test/Developer.test.js +34 -0
  105. package/test/Electron.test.js +93 -0
  106. package/test/Error.test.js +16 -0
  107. package/test/Exec.test.js +25 -0
  108. package/test/ExtensionHost.test.js +127 -0
  109. package/test/ExtensionHostColorTheme.test.js +141 -0
  110. package/test/ExtensionHostCommand.test.js +27 -0
  111. package/test/ExtensionHostCompletion.test.js +45 -0
  112. package/test/ExtensionHostDefinition.test.js +35 -0
  113. package/test/ExtensionHostDiagnostic.test.js +23 -0
  114. package/test/ExtensionHostFileSystem.test.js +91 -0
  115. package/test/ExtensionHostFormatting.test.js +27 -0
  116. package/test/ExtensionHostIconTheme.test.js +73 -0
  117. package/test/ExtensionHostLanguages.test.js +212 -0
  118. package/test/ExtensionHostOutput.test.js +23 -0
  119. package/test/ExtensionHostRename.test.js +59 -0
  120. package/test/ExtensionHostSemanticTokens.test.js +26 -0
  121. package/test/ExtensionHostTabCompletion.test.js +35 -0
  122. package/test/ExtensionHostTextDocument.test.js +61 -0
  123. package/test/ExtensionHostTypeDefinition.test.js +35 -0
  124. package/test/ExtensionHostWorkspace.test.js +21 -0
  125. package/test/ExtensionManagement.test.js +408 -0
  126. package/test/FileSystem.test.js +294 -0
  127. package/test/Json.test.js +25 -0
  128. package/test/Native.test.js +31 -0
  129. package/test/OutputChannel.test.js +68 -0
  130. package/test/Path.test.js +17 -0
  131. package/test/Platform.test.js +61 -0
  132. package/test/Preferences.test.js +140 -0
  133. package/test/Process.test.js +9 -0
  134. package/test/RgPath.test.js +5 -0
  135. package/test/Search.test.js +45 -0
  136. package/test/SearchFile.test.js +80 -0
  137. package/test/Terminal.test.js +53 -0
  138. package/test/Trash.test.js +34 -0
  139. package/test/WebSocketServer.test.js +98 -0
  140. package/test/fixture-search-1/index.html +10 -0
  141. package/test/fixture-search-file-1/fileA +0 -0
  142. package/test/fixture-search-file-1/fileB +0 -0
  143. package/test/fixture-search-file-1/nested/fileC +0 -0
@@ -0,0 +1,12 @@
1
+ export const executeImplementationProvider = async (
2
+ extensionHost,
3
+ documentId,
4
+ offset
5
+ ) => {
6
+ const implementations = await extensionHost.invoke(
7
+ 'Implementation.execute',
8
+ documentId,
9
+ offset
10
+ )
11
+ return implementations
12
+ }
@@ -0,0 +1,23 @@
1
+ import VError from 'verror'
2
+ import * as Callback from '../Callback/Callback.js'
3
+ import * as ChildProcess from '../ChildProcess/ChildProcess.js'
4
+ import * as Debug from '../Debug/Debug.js'
5
+ import * as Error from '../Error/Error.js'
6
+ import * as ExtensionManagement from '../ExtensionManagement/ExtensionManagement.js'
7
+ import * as ReadJson from '../JsonFile/JsonFile.js'
8
+ import * as JsonRpc from '../JsonRpc/JsonRpc.js'
9
+ import * as Path from '../Path/Path.js'
10
+ import * as Root from '../Root/Root.js'
11
+ import * as Socket from '../Socket/Socket.js'
12
+ import * as TextDocument from '../TextDocument/TextDocument.js'
13
+ import * as Platform from '../Platform/Platform.js'
14
+
15
+ const getExtensionKeyBindings = (extension) => {
16
+ return extension.keyBindings || []
17
+ }
18
+
19
+ export const getKeyBindings = async () => {
20
+ const extensions = await ExtensionManagement.getExtensions()
21
+ const keyBindings = extensions.flatMap(getExtensionKeyBindings)
22
+ return keyBindings
23
+ }
@@ -0,0 +1,84 @@
1
+ import { join } from 'node:path'
2
+ import { pathToFileURL } from 'node:url'
3
+ import VError from 'verror'
4
+ import * as Error from '../Error/Error.js'
5
+ import * as ExtensionManagement from '../ExtensionManagement/ExtensionManagement.js'
6
+ import * as ReadJson from '../JsonFile/JsonFile.js'
7
+ import * as Path from '../Path/Path.js'
8
+
9
+ const getExtensionLanguages = (extension) => {
10
+ if (!extension.languages) {
11
+ return []
12
+ }
13
+ return extension.languages.map((language) => ({}))
14
+ }
15
+
16
+ const toRemoteUrl = (path) => {
17
+ const url = pathToFileURL(path).toString().slice(8)
18
+ return `/remote/${url}`
19
+ }
20
+
21
+ const getLanguagesFromExtension = (extension) => {
22
+ // TODO what if extension is null? should not crash process, handle error gracefully
23
+ if (!extension.languages) {
24
+ return []
25
+ }
26
+ const getLanguageFromExtension = (language) => {
27
+ if (language.tokenize) {
28
+ if (typeof language.tokenize !== 'string') {
29
+ console.warn(
30
+ `[info] ${
31
+ language.id
32
+ }: language.tokenize must be of type string but was of type ${typeof language.tokenize}`
33
+ )
34
+ return {
35
+ ...language,
36
+ tokenize: '',
37
+ }
38
+ }
39
+ return {
40
+ ...language,
41
+ tokenize: toRemoteUrl(join(extension.path, language.tokenize)),
42
+ }
43
+ }
44
+ return language
45
+ }
46
+ return extension.languages.map(getLanguageFromExtension)
47
+ }
48
+
49
+ const getLanguagesFromExtensions = (extensions) => {
50
+ return extensions.flatMap(getLanguagesFromExtension)
51
+ }
52
+
53
+ export const getLanguages = async () => {
54
+ const extensions = await ExtensionManagement.getExtensions()
55
+ const languages = getLanguagesFromExtensions(extensions)
56
+ return languages
57
+ }
58
+
59
+ const getLanguageConfigurationPathFromExtensions = (extensions, languageId) => {
60
+ for (const extension of extensions) {
61
+ if (extension.languages && extension.languages) {
62
+ for (const language of extension.languages) {
63
+ if (language.id === languageId && language.configuration) {
64
+ return join(extension.path, language.configuration)
65
+ }
66
+ }
67
+ }
68
+ }
69
+ return ''
70
+ }
71
+
72
+ export const getLanguageConfiguration = async (languageId) => {
73
+ try {
74
+ const extensions = await ExtensionManagement.getExtensions()
75
+ const languageConfigurationPath =
76
+ getLanguageConfigurationPathFromExtensions(extensions, languageId)
77
+ return await ReadJson.readJson(languageConfigurationPath)
78
+ } catch (error) {
79
+ throw new VError(
80
+ error,
81
+ `Failed to load language configuration for ${languageId}`
82
+ )
83
+ }
84
+ }
@@ -0,0 +1,26 @@
1
+ import * as Debug from '../Debug/Debug.js'
2
+
3
+ export const didInstallExtension = (extensionHost, extension) => {
4
+ return extensionHost.invoke('didInstallExtension', extension)
5
+ }
6
+
7
+ export const didUninstallExtension = (extensionHost, extension) => {
8
+ return extensionHost.invoke('didUninstallExtension', extension)
9
+ }
10
+
11
+ export const disableExtension = (extensionHost, extension) => {
12
+ return extensionHost.invoke('disableExtension', extension)
13
+ }
14
+
15
+ // TODO have enableExtensionsCommand to avoid sending too many messages via ipc -> only send one ipc message to enable multiple extensions
16
+ export const enableExtension = async (extensionHost, extension) => {
17
+ Debug.debug(`ExtensionHost#enableExtension ${extension.id}`)
18
+ await extensionHost.invoke('enableExtension', extension)
19
+ }
20
+
21
+ export const activateAll = async (extensions) => {
22
+ // console.log({ extensions })
23
+ const results = await Promise.allSettled(extensions.map(enableExtension))
24
+ // console.log({ results })
25
+ return results
26
+ }
@@ -0,0 +1,3 @@
1
+ export const getOutputChannels = (extensionHost) => {
2
+ return extensionHost.invoke('OutputChannel.getOutputChannels')
3
+ }
@@ -0,0 +1,6 @@
1
+ export const handleQuickPickResult = (extensionHost, index) => {
2
+ return extensionHost.invoke(
3
+ 'ExtensionHostQuickPick.handleQuickPickResult',
4
+ index
5
+ )
6
+ }
@@ -0,0 +1,12 @@
1
+ export const executeReferenceProvider = async (
2
+ extensionHost,
3
+ documentId,
4
+ offset
5
+ ) => {
6
+ const references = await extensionHost.invoke(
7
+ 'References.execute',
8
+ documentId,
9
+ offset
10
+ )
11
+ return references
12
+ }
@@ -0,0 +1,27 @@
1
+ export const executePrepareRename = async (
2
+ extensionHost,
3
+ documentId,
4
+ offset
5
+ ) => {
6
+ const result = await extensionHost.invoke(
7
+ 'Rename.executePrepareRename',
8
+ documentId,
9
+ offset
10
+ )
11
+ return result
12
+ }
13
+
14
+ export const executeRename = async (
15
+ extensionHost,
16
+ documentId,
17
+ offset,
18
+ newName
19
+ ) => {
20
+ const result = await extensionHost.invoke(
21
+ 'Rename.executeRename',
22
+ documentId,
23
+ offset,
24
+ newName
25
+ )
26
+ return result
27
+ }
@@ -0,0 +1,10 @@
1
+ export const executeSemanticTokenProvider = async (
2
+ extensionHost,
3
+ documentId
4
+ ) => {
5
+ const semanticTokens = await extensionHost.invoke(
6
+ 'SemanticTokens.execute',
7
+ documentId
8
+ )
9
+ return semanticTokens
10
+ }
@@ -0,0 +1,15 @@
1
+ export const sourceControlAcceptInput = (extensionHost, text) => {
2
+ return extensionHost.invoke('SourceControl.acceptInput', text)
3
+ }
4
+
5
+ export const getSourceControlChangedFiles = async (extensionHost) => {
6
+ const changedFiles = await extensionHost.invoke(
7
+ 'getSourceControlChangedFiles'
8
+ )
9
+ return changedFiles
10
+ }
11
+
12
+ export const getSourceControlBadgeCount = async (extensionHost) => {
13
+ const count = await extensionHost.invoke('getSourceControlBadgeCount')
14
+ return count
15
+ }
@@ -0,0 +1,3 @@
1
+ export const getMemoryUsage = (extensionHost) => {
2
+ return extensionHost.invoke('getMemoryUsage')
3
+ }
@@ -0,0 +1,7 @@
1
+ export const getStatusBarItems = (extensionHost) => {
2
+ return extensionHost.invoke('StatusBar.getStatusBarItems')
3
+ }
4
+
5
+ export const registerChangeListener = (extensionHost, id) => {
6
+ return extensionHost.invoke('StatusBar.registerChangeListener', id)
7
+ }
@@ -0,0 +1,12 @@
1
+ export const executeTabCompletionProvider = async (
2
+ extensionHost,
3
+ documentId,
4
+ offset
5
+ ) => {
6
+ const tabCompletion = await extensionHost.invoke(
7
+ 'executeTabCompletionProvider',
8
+ documentId,
9
+ offset
10
+ )
11
+ return tabCompletion
12
+ }
@@ -0,0 +1,35 @@
1
+ export const textDocumentSyncInitial = (
2
+ extensionHost,
3
+ uri,
4
+ textDocumentId,
5
+ languageId,
6
+ text
7
+ ) => {
8
+ return extensionHost.invoke(
9
+ 'TextDocument.syncInitial',
10
+ uri,
11
+ textDocumentId,
12
+ languageId,
13
+ text
14
+ )
15
+ }
16
+
17
+ export const textDocumentSyncIncremental = (
18
+ extensionHost,
19
+ textDocumentId,
20
+ changes
21
+ ) => {
22
+ return extensionHost.invoke(
23
+ 'TextDocument.syncIncremental',
24
+ textDocumentId,
25
+ changes
26
+ )
27
+ }
28
+
29
+ export const setLanguageId = (extensionHost, textDocumentId, languageId) => {
30
+ return extensionHost.invoke(
31
+ 'TextDocument.setLanguageId',
32
+ textDocumentId,
33
+ languageId
34
+ )
35
+ }
@@ -0,0 +1,12 @@
1
+ export const executeTypeDefinitionProvider = async (
2
+ extensionHost,
3
+ textDocumentId,
4
+ offset
5
+ ) => {
6
+ const result = await extensionHost.invoke(
7
+ 'TypeDefinition.execute',
8
+ textDocumentId,
9
+ offset
10
+ )
11
+ return result
12
+ }
@@ -0,0 +1,3 @@
1
+ export const setWorkspacePath = (extensionHost, path) => {
2
+ return extensionHost.invoke('Workspace.setWorkspacePath', path)
3
+ }
@@ -0,0 +1,11 @@
1
+ import * as Command from '../Command/Command.js'
2
+ import * as ExtensionManagement from './ExtensionManagement.js'
3
+
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)
11
+ }
@@ -0,0 +1,319 @@
1
+ import { mkdir, readdir, rename, rm } from 'node:fs/promises'
2
+ import { performance } from 'node:perf_hooks'
3
+ import isObject from 'is-object'
4
+ import VError from 'verror'
5
+ import * as Debug from '../Debug/Debug.js'
6
+ import * as Error from '../Error/Error.js'
7
+ import * as ReadJson from '../JsonFile/JsonFile.js'
8
+ import * as Path from '../Path/Path.js'
9
+ import * as Platform from '../Platform/Platform.js'
10
+ import * as Queue from '../Queue/Queue.js'
11
+
12
+ export const state = {
13
+ async getExtensions() {
14
+ if (process.env.ONLY_EXTENSION) {
15
+ return getExtensionManifests([process.env.ONLY_EXTENSION])
16
+ }
17
+ const builtinExtensions = await getBuiltinExtensions()
18
+ const installedExtensions = await getInstalledExtensions()
19
+ return [...builtinExtensions, ...installedExtensions]
20
+ },
21
+ async getThemeExtensions() {
22
+ const builtinExtensionsPath = Platform.getBuiltinExtensionsPath()
23
+ const dirents = await readdir(builtinExtensionsPath)
24
+ const paths = []
25
+ for (const dirent of dirents) {
26
+ if (isTheme(dirent)) {
27
+ paths.push(getAbsoluteBuiltinExtensionPath(dirent))
28
+ }
29
+ }
30
+ if (process.env.ONLY_EXTENSION && isTheme(process.env.ONLY_EXTENSION)) {
31
+ paths.push(process.env.ONLY_EXTENSION)
32
+ }
33
+ const builtinExtensions = await getExtensionManifests(paths)
34
+ return builtinExtensions
35
+ },
36
+ }
37
+
38
+ export const install = async (id) => {
39
+ // TODO this should be a stateless function, renderer-worker should have info on marketplace url
40
+ // TODO use command.execute
41
+ try {
42
+ const { download, extract } = await import('../Download/Download.js')
43
+ const marketplaceUrl = Platform.getMarketplaceUrl()
44
+ Debug.debug(`ExtensionManagement#install ${id}`)
45
+ const cachedExtensionsPath = Platform.getCachedExtensionsPath()
46
+ const extensionsPath = Platform.getExtensionsPath()
47
+ // TODO maybe a queue is over engineering here
48
+ await Queue.add('download', async () => {
49
+ Debug.debug(`ExtensionManagement#download ${id}`)
50
+ // TODO use command.execute
51
+ await download(
52
+ `${marketplaceUrl}/download/${id}`,
53
+ Path.join(cachedExtensionsPath, `${id}.tar.br`)
54
+ )
55
+ Debug.debug(`ExtensionManagement#extract ${id}`)
56
+ await extract(
57
+ Path.join(cachedExtensionsPath, `${id}.tar.br`),
58
+ Path.join(extensionsPath, id)
59
+ )
60
+ })
61
+ // TODO should this be here? (probably not)
62
+ // await ExtensionHost.enableExtension({
63
+ // id,
64
+ // type: 'marketplace',
65
+ // })
66
+ } catch (error) {
67
+ throw new Error.OperationalError({
68
+ cause: error,
69
+ code: 'E_EXT_INSTALL_FAILED',
70
+ message: `Failed to install extension "${id}"`,
71
+ })
72
+ }
73
+ }
74
+
75
+ export const uninstall = async (id) => {
76
+ try {
77
+ Debug.debug(`ExtensionManagement#uninstall ${id}`)
78
+ const extensionsPath = Platform.getExtensionsPath()
79
+ await rm(Path.join(extensionsPath, id), { recursive: true })
80
+ } catch (error) {
81
+ // if (error.code === 'ENOENT') {
82
+ // return
83
+ // }
84
+ throw new Error.OperationalError({
85
+ cause: error,
86
+ code: 'E_EXT_UNINSTALL_FAILED',
87
+ message: `Failed to uninstall extension "${id}"`,
88
+ })
89
+ }
90
+ }
91
+
92
+ export const enable = async (id) => {
93
+ try {
94
+ Debug.debug(`ExtensionManagement#enable ${id}`)
95
+ const extensionsPath = Platform.getExtensionsPath()
96
+ const disabledExtensionsPath = Platform.getDisabledExtensionsPath()
97
+ await mkdir(extensionsPath, { recursive: true })
98
+ await rename(
99
+ Path.join(disabledExtensionsPath, id),
100
+ Path.join(extensionsPath, id)
101
+ )
102
+ } catch (error) {
103
+ throw new Error.OperationalError({
104
+ cause: error,
105
+ message: `Failed to enable extension "${id}"`,
106
+ code: 'E_EXTENSION_ENABLING_FAILED',
107
+ })
108
+ }
109
+ }
110
+
111
+ export const disable = async (id) => {
112
+ try {
113
+ Debug.debug(`ExtensionManagement#disable ${id}`)
114
+ const disabledExtensionsPath = Platform.getDisabledExtensionsPath()
115
+ const extensionsPath = Platform.getExtensionsPath()
116
+ await mkdir(disabledExtensionsPath, { recursive: true })
117
+ await rename(
118
+ Path.join(extensionsPath, id),
119
+ Path.join(disabledExtensionsPath, id)
120
+ )
121
+ } catch (error) {
122
+ throw new Error.OperationalError({
123
+ cause: error,
124
+ code: 'E_EXTENSION_DISABLING_FAILED',
125
+ message: `Failed to disable extension ${id}`,
126
+ })
127
+ }
128
+ }
129
+
130
+ const getAbsoluteDisabledPath = (dirent) => {
131
+ const disabledExtensionsPath = Platform.getDisabledExtensionsPath()
132
+ return Path.join(disabledExtensionsPath, dirent)
133
+ }
134
+
135
+ const getDisabledExtensionPaths = async () => {
136
+ try {
137
+ const disabledExtensionsPaths = Platform.getDisabledExtensionsPath()
138
+ const dirents = await readdir(disabledExtensionsPaths)
139
+ const paths = dirents.map(getAbsoluteDisabledPath)
140
+ return paths
141
+ } catch {
142
+ // TODO handle error
143
+ return []
144
+ }
145
+ }
146
+
147
+ const getAbsoluteBuiltinExtensionPath = (dirent) => {
148
+ const builtinExtensionsPath = Platform.getBuiltinExtensionsPath()
149
+ return Path.join(builtinExtensionsPath, dirent)
150
+ }
151
+
152
+ const isIncludedBuiltinExtension = (dirent) => {
153
+ const SKIPPED = [
154
+ 'hello-world',
155
+ 'vite',
156
+ 'soundcloud',
157
+ 'npm',
158
+ // 'builtin.git', ~40MB memory usage (too many file watchers)
159
+ // 'builtin.prettier', ~20MB memory usage (maybe lazyload prettier)
160
+ // 'language-features-typescript',
161
+ // 'builtin.vscode-icons',
162
+ ]
163
+ return !SKIPPED.includes(dirent)
164
+ }
165
+
166
+ const getBuiltinExtensionPaths = async () => {
167
+ try {
168
+ const builtinExtensionsPath = Platform.getBuiltinExtensionsPath()
169
+ const dirents = await readdir(builtinExtensionsPath)
170
+ const filteredDirents = dirents.filter(isIncludedBuiltinExtension)
171
+ const paths = filteredDirents.map(getAbsoluteBuiltinExtensionPath)
172
+ return paths
173
+ } catch {
174
+ console.info('no builtin extension paths found')
175
+ return []
176
+ }
177
+ }
178
+
179
+ const getAbsoluteInstalledExtensionPath = (dirent) => {
180
+ const extensionsPath = Platform.getExtensionsPath()
181
+ return Path.join(extensionsPath, dirent)
182
+ }
183
+
184
+ const getInstalledExtensionPaths = async () => {
185
+ try {
186
+ const extensionsPath = Platform.getExtensionsPath()
187
+ const dirents = await readdir(extensionsPath)
188
+ const paths = dirents.map(getAbsoluteInstalledExtensionPath)
189
+ return paths
190
+ } catch (error) {
191
+ // TODO how to make typescript happy?
192
+ // @ts-ignore
193
+ if (error.code === 'ENOENT') {
194
+ // TODO what if mkdir fails?
195
+ await mkdir(Platform.getExtensionsPath(), { recursive: true })
196
+ return []
197
+ }
198
+ throw new Error.OperationalError({
199
+ cause: error,
200
+ code: 'E_FAILED_TO_LOAD_INSTALLED_EXTENSIONS',
201
+ message: 'Failed to get installed extensions',
202
+ })
203
+ }
204
+ }
205
+
206
+ const RE_EXTENSION_FRAGMENT = /.+(\/|\\)(.+)$/
207
+
208
+ const inferExtensionId = (absolutePath) => {
209
+ const match = absolutePath.match(RE_EXTENSION_FRAGMENT)
210
+ if (match) {
211
+ return match[2]
212
+ }
213
+ return ''
214
+ }
215
+
216
+ // TODO json parsing and error handling should happen in renderer process
217
+ const getExtensionManifest = async (path) => {
218
+ try {
219
+ const absolutePath = Path.join(path, 'extension.json')
220
+ const json = await ReadJson.readJson(absolutePath)
221
+ if (!isObject(json)) {
222
+ // TODO should include stack of extension json file here
223
+ throw new VError('Invalid manifest file: Not an JSON object.')
224
+ }
225
+ return {
226
+ ...json,
227
+ path,
228
+ status: 'fulfilled',
229
+ }
230
+ } catch (error) {
231
+ const id = inferExtensionId(path)
232
+ return {
233
+ path,
234
+ status: 'rejected',
235
+ reason: new Error.OperationalError({
236
+ cause: error,
237
+ code: 'E_LOADING_EXTENSION_MANIFEST_FAilED',
238
+ message: `Failed to load extension "${id}": Failed to load extension manifest`,
239
+ // @ts-ignore
240
+ stack: error.stack,
241
+ }),
242
+ }
243
+ }
244
+ }
245
+
246
+ const isRejected = (promise) => {
247
+ return promise.status === 'rejected'
248
+ }
249
+
250
+ /**
251
+ * @param {string[]} paths
252
+ */
253
+ const getExtensionManifests = async (paths) => {
254
+ const allResults = await Promise.all(paths.map(getExtensionManifest))
255
+ return allResults
256
+ }
257
+
258
+ export const getBuiltinExtensions = async () => {
259
+ const builtinExtensionPaths = await getBuiltinExtensionPaths()
260
+ const builtinExtensions = await getExtensionManifests(builtinExtensionPaths)
261
+ return builtinExtensions
262
+ }
263
+
264
+ export const getInstalledExtensions = async () => {
265
+ const installedExtensionPaths = await getInstalledExtensionPaths()
266
+ const installedExtensions = await getExtensionManifests(
267
+ installedExtensionPaths
268
+ )
269
+ return installedExtensions
270
+ }
271
+
272
+ export const getExtensions = async () => {
273
+ return state.getExtensions()
274
+ }
275
+
276
+ const RE_THEME = /[a-z]+\.[a-z\-]*theme-[a-z\d\-]+$/
277
+ const isTheme = (extensionId) => {
278
+ return RE_THEME.test(extensionId)
279
+ }
280
+
281
+ const getThemeExtensionsBuiltin = async () => {
282
+ const builtinExtensionsPath = Platform.getBuiltinExtensionsPath()
283
+ const dirents = await readdir(builtinExtensionsPath)
284
+ const filteredDirents = dirents.filter(isTheme)
285
+ const paths = filteredDirents.map(getAbsoluteBuiltinExtensionPath)
286
+ }
287
+
288
+ export const getThemeExtensions = async () => {
289
+ return state.getThemeExtensions()
290
+ }
291
+
292
+ export const getDisabledExtensions = async () => {
293
+ const disabledExtensionPaths = await getDisabledExtensionPaths()
294
+ const disabledExtensions = await getExtensionManifests(disabledExtensionPaths)
295
+ return disabledExtensions
296
+ }
297
+
298
+ export const getAllExtensions = async () => {
299
+ if (process.env.ONLY_EXTENSION) {
300
+ return getExtensionManifests([process.env.ONLY_EXTENSION])
301
+ }
302
+ const t1 = performance.now()
303
+ const [builtinExtensions, installedExtensions, disabledExtensions] =
304
+ // TODO handle error when one of them fails
305
+ await Promise.all([
306
+ getBuiltinExtensions(),
307
+ getInstalledExtensions(),
308
+ getDisabledExtensions(),
309
+ ])
310
+ const t4 = performance.now()
311
+ // TODO can optimize this by loading extension manifest while loading paths (though 2ms isn't a bottleneck right now)
312
+ // e.g.
313
+ // await Promise.all([getBuiltinExtensionPaths.then(getExtensionManifests), getInstalledExtensionPaths.then(getExtensionManifests)])
314
+ const timings = {
315
+ total: t4 - t1,
316
+ }
317
+ // console.log(timings)
318
+ return [...builtinExtensions, ...installedExtensions, ...disabledExtensions]
319
+ }
@@ -0,0 +1,32 @@
1
+ import * as Command from '../Command/Command.js'
2
+ import * as FileSystem from './FileSystem.js'
3
+
4
+ // TODO ugly code here -> should be inside FileSystem.js
5
+ const fileSystemWatch = (socket, id, path) => {
6
+ // const watcher = FileSystem.watch(path, {
7
+ // onAll(...args) {
8
+ // socket.send(JSON.stringify([/* callback */ id, /* event */ args]))
9
+
10
+ // }
11
+ // }
12
+ // )
13
+ // socket.on('close', () => {
14
+ // watcher.close()
15
+ // })
16
+ }
17
+
18
+ // 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)
32
+ }