@lvce-editor/shared-process 0.7.2 → 0.7.4
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/config/defaultKeyBindings.json +40 -0
- package/package.json +3 -3
- package/src/parts/Cli/Cli.js +2 -0
- package/src/parts/CliUnlink/CliUnlink.js +6 -0
- package/src/parts/Error/FileNotFoundError.js +2 -0
- package/src/parts/ExtensionHostRpc/ExtensionHostRpc.js +1 -1
- package/src/parts/ExtensionLink/ExtensionLink.js +15 -12
- package/src/parts/ExtensionList/ExtensionList.js +26 -31
- package/src/parts/ExtensionManagement/ExtensionManagement.js +45 -105
- package/src/parts/ExtensionManagement/ExtensionManagementColorTheme.js +1 -1
- package/src/parts/ExtensionManifest/ExtensionManifest.js +51 -0
- package/src/parts/ExtensionManifestInputType/ExtensionManifestInputType.js +3 -0
- package/src/parts/ExtensionManifests/ExtensionManifests.js +39 -0
- package/src/parts/ExtensionManifests/ExtensionManifestsFromFolder.js +34 -0
- package/src/parts/ExtensionManifests/ExtensionManifestsFromOnlyExtension.js +9 -0
- package/src/parts/ExtensionUnlink/ExtensionUnlink.js +19 -0
- package/src/parts/FileSystem/FileSystem.js +12 -0
- package/src/parts/FileSystemErrorCodes/FileSystemErrorCodes.js +4 -0
- package/src/parts/Platform/Platform.js +4 -0
- package/src/parts/SymLink/SymLink.js +3 -1
- package/src/sharedProcessMain.js +1 -1
|
@@ -697,5 +697,45 @@
|
|
|
697
697
|
{
|
|
698
698
|
"key": "ctrl+-",
|
|
699
699
|
"command": "Window.zoomOut"
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
"key": "ArrowDown",
|
|
703
|
+
"command": "TitleBarMenuBar.handleKeyArrowDown",
|
|
704
|
+
"when": "focus.TitleBarMenuBar"
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
"key": "ArrowUp",
|
|
708
|
+
"command": "TitleBarMenuBar.handleKeyArrowUp",
|
|
709
|
+
"when": "focus.TitleBarMenuBar"
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
"key": "ArrowRight",
|
|
713
|
+
"command": "TitleBarMenuBar.handleKeyArrowRight",
|
|
714
|
+
"when": "focus.TitleBarMenuBar"
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
"key": "ArrowLeft",
|
|
718
|
+
"command": "TitleBarMenuBar.handleKeyArrowLeft",
|
|
719
|
+
"when": "focus.TitleBarMenuBar"
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
"key": "Space",
|
|
723
|
+
"command": "TitleBarMenuBar.handleKeySpace",
|
|
724
|
+
"when": "focus.TitleBarMenuBar"
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
"key": "Home",
|
|
728
|
+
"command": "TitleBarMenuBar.handleKeyHome",
|
|
729
|
+
"when": "focus.TitleBarMenuBar"
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
"key": "End",
|
|
733
|
+
"command": "TitleBarMenuBar.handleKeyEnd",
|
|
734
|
+
"when": "focus.TitleBarMenuBar"
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
"key": "Escape",
|
|
738
|
+
"command": "TitleBarMenuBar.handleKeyEscape",
|
|
739
|
+
"when": "focus.TitleBarMenuBar"
|
|
700
740
|
}
|
|
701
741
|
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lvce-editor/shared-process",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"node": ">=16"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@lvce-editor/extension-host": "0.7.
|
|
24
|
-
"@lvce-editor/pty-host": "0.7.
|
|
23
|
+
"@lvce-editor/extension-host": "0.7.4",
|
|
24
|
+
"@lvce-editor/pty-host": "0.7.4",
|
|
25
25
|
"chokidar": "^3.5.3",
|
|
26
26
|
"debug": "^4.3.4",
|
|
27
27
|
"execa": "^6.1.0",
|
package/src/parts/Cli/Cli.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import VError from 'verror'
|
|
2
|
+
import * as FileSystemErrorCodes from '../FileSystemErrorCodes/FileSystemErrorCodes.js'
|
|
2
3
|
|
|
3
4
|
export class FileNotFoundError extends VError {
|
|
4
5
|
constructor(path) {
|
|
5
6
|
super(`File not found '${path}'`)
|
|
6
7
|
this.name = 'FileNotFoundError'
|
|
8
|
+
this.code = FileSystemErrorCodes.ENOENT
|
|
7
9
|
}
|
|
8
10
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import VError from 'verror'
|
|
2
|
+
import * as ExtensionManifest from '../ExtensionManifest/ExtensionManifest.js'
|
|
3
|
+
import * as ExtensionManifestStatus from '../ExtensionManifestStatus/ExtensionManifestStatus.js'
|
|
2
4
|
import * as FileSystem from '../FileSystem/FileSystem.js'
|
|
3
5
|
import * as FileSystemErrorCodes from '../FileSystemErrorCodes/FileSystemErrorCodes.js'
|
|
4
6
|
import * as Path from '../Path/Path.js'
|
|
@@ -7,29 +9,30 @@ import * as SymLink from '../SymLink/SymLink.js'
|
|
|
7
9
|
|
|
8
10
|
const linkFallBack = async (path) => {
|
|
9
11
|
try {
|
|
10
|
-
const
|
|
11
|
-
if (
|
|
12
|
-
throw
|
|
12
|
+
const manifest = await ExtensionManifest.get(path)
|
|
13
|
+
if (manifest.status === ExtensionManifestStatus.Rejected) {
|
|
14
|
+
throw manifest.reason
|
|
13
15
|
}
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const to = Path.join(
|
|
16
|
+
const linkedExtensionsPath = Platform.getLinkedExtensionsPath()
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
const to = Path.join(linkedExtensionsPath, manifest.id)
|
|
17
19
|
await FileSystem.remove(to)
|
|
18
20
|
await SymLink.createSymLink(path, to)
|
|
19
21
|
} catch (error) {
|
|
22
|
+
console.log({ error })
|
|
20
23
|
throw new VError(error, `Failed to link extension`)
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
export const link = async (path) => {
|
|
25
28
|
try {
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
throw
|
|
29
|
+
const manifest = await ExtensionManifest.get(path)
|
|
30
|
+
if (manifest.status === ExtensionManifestStatus.Rejected) {
|
|
31
|
+
throw manifest.reason
|
|
29
32
|
}
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
const to = Path.join(
|
|
33
|
+
const linkedExtensionsPath = Platform.getLinkedExtensionsPath()
|
|
34
|
+
// @ts-ignore
|
|
35
|
+
const to = Path.join(linkedExtensionsPath, manifest.id)
|
|
33
36
|
await SymLink.createSymLink(path, to)
|
|
34
37
|
} catch (error) {
|
|
35
38
|
if (error && error.code === FileSystemErrorCodes.EEXIST) {
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { basename, dirname, join } from 'node:path'
|
|
1
|
+
import { basename } from 'node:path'
|
|
3
2
|
import VError from 'verror'
|
|
3
|
+
import * as ExtensionManifestInputType from '../ExtensionManifestInputType/ExtensionManifestInputType.js'
|
|
4
|
+
import * as ExtensionManifests from '../ExtensionManifests/ExtensionManifests.js'
|
|
4
5
|
import * as Platform from '../Platform/Platform.js'
|
|
5
|
-
import * as
|
|
6
|
-
|
|
7
|
-
const extensionPath = Platform.getExtensionsPath()
|
|
8
|
-
|
|
9
|
-
const getAbsolutePath = (dirent) => {
|
|
10
|
-
return join(extensionPath, dirent)
|
|
11
|
-
}
|
|
6
|
+
import * as ExtensionManifestStatus from '../ExtensionManifestStatus/ExtensionManifestStatus.js'
|
|
12
7
|
|
|
13
8
|
const getManifestVersion = (json) => {
|
|
14
9
|
if (json && json.version && typeof json.version === 'string') {
|
|
@@ -17,17 +12,15 @@ const getManifestVersion = (json) => {
|
|
|
17
12
|
return 'n/a'
|
|
18
13
|
}
|
|
19
14
|
|
|
20
|
-
const getManifestId = (json
|
|
15
|
+
const getManifestId = (json) => {
|
|
21
16
|
if (json && json.id && typeof json.id === 'string') {
|
|
22
17
|
return json.id
|
|
23
18
|
}
|
|
24
|
-
return basename(
|
|
19
|
+
return basename(json.path)
|
|
25
20
|
}
|
|
26
21
|
|
|
27
|
-
const getManifestInfo =
|
|
28
|
-
const
|
|
29
|
-
const json = await JsonFile.readJson(manifestPath)
|
|
30
|
-
const id = getManifestId(json, extensionPath)
|
|
22
|
+
const getManifestInfo = (json) => {
|
|
23
|
+
const id = getManifestId(json)
|
|
31
24
|
const version = getManifestVersion(json)
|
|
32
25
|
return {
|
|
33
26
|
id,
|
|
@@ -36,31 +29,33 @@ const getManifestInfo = async (extensionPath) => {
|
|
|
36
29
|
}
|
|
37
30
|
|
|
38
31
|
const isFulfilled = (result) => {
|
|
39
|
-
return result.status ===
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const getValue = (result) => {
|
|
43
|
-
return result.value
|
|
32
|
+
return result.status === ExtensionManifestStatus.Resolved
|
|
44
33
|
}
|
|
45
34
|
|
|
46
|
-
const getManifestInfos =
|
|
47
|
-
const
|
|
48
|
-
manifestPaths.map(getManifestInfo)
|
|
49
|
-
)
|
|
50
|
-
const results = manifestInfos.filter(isFulfilled).map(getValue)
|
|
35
|
+
const getManifestInfos = (manifests) => {
|
|
36
|
+
const results = manifests.filter(isFulfilled).map(getManifestInfo)
|
|
51
37
|
return results
|
|
52
38
|
}
|
|
53
39
|
|
|
54
40
|
export const list = async () => {
|
|
55
41
|
try {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
42
|
+
const manifests = await ExtensionManifests.getAll([
|
|
43
|
+
{
|
|
44
|
+
type: ExtensionManifestInputType.Folder,
|
|
45
|
+
path: Platform.getLinkedExtensionsPath(),
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: ExtensionManifestInputType.Folder,
|
|
49
|
+
path: Platform.getExtensionsPath(),
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
type: ExtensionManifestInputType.Folder,
|
|
53
|
+
path: Platform.getBuiltinExtensionsPath(),
|
|
54
|
+
},
|
|
55
|
+
])
|
|
56
|
+
const infos = getManifestInfos(manifests)
|
|
59
57
|
return infos
|
|
60
58
|
} catch (error) {
|
|
61
|
-
if (error && error.code === 'ENOENT') {
|
|
62
|
-
return []
|
|
63
|
-
}
|
|
64
59
|
throw new VError(error, `Failed to list extensions`)
|
|
65
60
|
}
|
|
66
61
|
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import isObject from 'is-object'
|
|
2
1
|
import { mkdir, readdir, rename, rm } from 'node:fs/promises'
|
|
3
|
-
import path from 'node:path'
|
|
4
2
|
import { performance } from 'node:perf_hooks'
|
|
5
3
|
import VError from 'verror'
|
|
6
4
|
import * as Debug from '../Debug/Debug.js'
|
|
7
|
-
import * as
|
|
5
|
+
import * as ExtensionManifestInputType from '../ExtensionManifestInputType/ExtensionManifestInputType.js'
|
|
6
|
+
import * as ExtensionManifests from '../ExtensionManifests/ExtensionManifests.js'
|
|
8
7
|
import * as Path from '../Path/Path.js'
|
|
9
8
|
import * as Platform from '../Platform/Platform.js'
|
|
10
9
|
import * as Queue from '../Queue/Queue.js'
|
|
11
|
-
import * as ExtensionManifestStatus from '../ExtensionManifestStatus/ExtensionManifestStatus.js'
|
|
12
10
|
|
|
13
11
|
const isLanguageBasics = (extension) => {
|
|
14
12
|
if (extension && extension.id) {
|
|
@@ -183,121 +181,63 @@ const getInstalledExtensionPaths = async () => {
|
|
|
183
181
|
}
|
|
184
182
|
}
|
|
185
183
|
|
|
186
|
-
const RE_EXTENSION_FRAGMENT = /.+(\/|\\)(.+)$/
|
|
187
|
-
|
|
188
|
-
const inferExtensionId = (absolutePath) => {
|
|
189
|
-
const match = absolutePath.match(RE_EXTENSION_FRAGMENT)
|
|
190
|
-
if (match) {
|
|
191
|
-
return match[2]
|
|
192
|
-
}
|
|
193
|
-
return ''
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// TODO json parsing and error handling should happen in renderer process
|
|
197
|
-
const getExtensionManifest = async (path) => {
|
|
198
|
-
try {
|
|
199
|
-
const absolutePath = Path.join(path, 'extension.json')
|
|
200
|
-
const json = await ReadJson.readJson(absolutePath)
|
|
201
|
-
if (!isObject(json)) {
|
|
202
|
-
// TODO should include stack of extension json file here
|
|
203
|
-
throw new VError('Invalid manifest file: Not an JSON object.')
|
|
204
|
-
}
|
|
205
|
-
return {
|
|
206
|
-
...json,
|
|
207
|
-
path,
|
|
208
|
-
status: ExtensionManifestStatus.Resolved,
|
|
209
|
-
}
|
|
210
|
-
} catch (error) {
|
|
211
|
-
const id = inferExtensionId(path)
|
|
212
|
-
return {
|
|
213
|
-
path,
|
|
214
|
-
status: ExtensionManifestStatus.Rejected,
|
|
215
|
-
reason: new VError(
|
|
216
|
-
error,
|
|
217
|
-
`Failed to load extension "${id}": Failed to load extension manifest`
|
|
218
|
-
),
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* @param {string[]} paths
|
|
225
|
-
*/
|
|
226
|
-
const getExtensionManifests = async (paths) => {
|
|
227
|
-
const allResults = await Promise.all(paths.map(getExtensionManifest))
|
|
228
|
-
return allResults
|
|
229
|
-
}
|
|
230
|
-
|
|
231
184
|
export const getBuiltinExtensions = async () => {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
185
|
+
return ExtensionManifests.getAll([
|
|
186
|
+
{
|
|
187
|
+
type: ExtensionManifestInputType.Folder,
|
|
188
|
+
path: Platform.getBuiltinExtensionsPath(),
|
|
189
|
+
},
|
|
190
|
+
])
|
|
235
191
|
}
|
|
236
192
|
|
|
237
|
-
export const getInstalledExtensions =
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
193
|
+
export const getInstalledExtensions = () => {
|
|
194
|
+
return ExtensionManifests.getAll([
|
|
195
|
+
{
|
|
196
|
+
type: ExtensionManifestInputType.Folder,
|
|
197
|
+
path: Platform.getExtensionsPath(),
|
|
198
|
+
},
|
|
199
|
+
])
|
|
243
200
|
}
|
|
244
201
|
|
|
245
202
|
export const getExtensions = async () => {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
return RE_THEME.test(extensionId)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const getThemeExtensionsBuiltin = async () => {
|
|
268
|
-
const builtinExtensionsPath = Platform.getBuiltinExtensionsPath()
|
|
269
|
-
const dirents = await readdir(builtinExtensionsPath)
|
|
270
|
-
const filteredDirents = dirents.filter(isTheme)
|
|
271
|
-
const paths = filteredDirents.map(getAbsoluteBuiltinExtensionPath)
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
export const getThemeExtensions = async () => {
|
|
275
|
-
const builtinExtensionsPath = Platform.getBuiltinExtensionsPath()
|
|
276
|
-
const dirents = await readdir(builtinExtensionsPath)
|
|
277
|
-
const paths = []
|
|
278
|
-
for (const dirent of dirents) {
|
|
279
|
-
if (isTheme(dirent)) {
|
|
280
|
-
paths.push(getAbsoluteBuiltinExtensionPath(dirent))
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
const onlyExtensionPath = Platform.getOnlyExtensionPath()
|
|
284
|
-
if (onlyExtensionPath && isTheme(onlyExtensionPath)) {
|
|
285
|
-
paths.push(onlyExtensionPath)
|
|
286
|
-
}
|
|
287
|
-
const builtinExtensions = await getExtensionManifests(paths)
|
|
288
|
-
return builtinExtensions
|
|
203
|
+
return ExtensionManifests.getAll([
|
|
204
|
+
{
|
|
205
|
+
type: ExtensionManifestInputType.OnlyExtension,
|
|
206
|
+
path: Platform.getOnlyExtensionPath(),
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
type: ExtensionManifestInputType.Folder,
|
|
210
|
+
path: Platform.getLinkedExtensionsPath(),
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
type: ExtensionManifestInputType.Folder,
|
|
214
|
+
path: Platform.getExtensionsPath(),
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
type: ExtensionManifestInputType.Folder,
|
|
218
|
+
path: Platform.getBuiltinExtensionsPath(),
|
|
219
|
+
},
|
|
220
|
+
])
|
|
289
221
|
}
|
|
290
222
|
|
|
291
223
|
export const getDisabledExtensions = async () => {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
224
|
+
return ExtensionManifests.getAll([
|
|
225
|
+
{
|
|
226
|
+
type: ExtensionManifestInputType.Folder,
|
|
227
|
+
path: Platform.getDisabledExtensionsPath(),
|
|
228
|
+
},
|
|
229
|
+
])
|
|
295
230
|
}
|
|
296
231
|
|
|
297
232
|
export const getAllExtensions = async () => {
|
|
298
233
|
const onlyExtensionPath = Platform.getOnlyExtensionPath()
|
|
299
234
|
if (onlyExtensionPath) {
|
|
300
|
-
return
|
|
235
|
+
return ExtensionManifests.getAll([
|
|
236
|
+
{
|
|
237
|
+
type: ExtensionManifestInputType.OnlyExtension,
|
|
238
|
+
path: onlyExtensionPath,
|
|
239
|
+
},
|
|
240
|
+
])
|
|
301
241
|
}
|
|
302
242
|
const t1 = performance.now()
|
|
303
243
|
const [builtinExtensions, installedExtensions, disabledExtensions] =
|
|
@@ -7,7 +7,7 @@ import * as ExtensionManagement from './ExtensionManagement.js'
|
|
|
7
7
|
// TODO test this function
|
|
8
8
|
// TODO very similar with getIconTheme
|
|
9
9
|
export const getColorThemeJson = async (colorThemeId) => {
|
|
10
|
-
const extensions = await ExtensionManagement.
|
|
10
|
+
const extensions = await ExtensionManagement.getExtensions()
|
|
11
11
|
for (const extension of extensions) {
|
|
12
12
|
if (!extension.colorThemes) {
|
|
13
13
|
continue
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import isObject from 'is-object'
|
|
2
|
+
import VError from 'verror'
|
|
3
|
+
import * as ExtensionManifestStatus from '../ExtensionManifestStatus/ExtensionManifestStatus.js'
|
|
4
|
+
import * as ReadJson from '../JsonFile/JsonFile.js'
|
|
5
|
+
import * as Path from '../Path/Path.js'
|
|
6
|
+
import * as FileSystemErrorCodes from '../FileSystemErrorCodes/FileSystemErrorCodes.js'
|
|
7
|
+
|
|
8
|
+
const RE_EXTENSION_FRAGMENT = /.+(\/|\\)(.+)$/
|
|
9
|
+
|
|
10
|
+
const inferExtensionId = (absolutePath) => {
|
|
11
|
+
const match = absolutePath.match(RE_EXTENSION_FRAGMENT)
|
|
12
|
+
if (match) {
|
|
13
|
+
return match[2]
|
|
14
|
+
}
|
|
15
|
+
return ''
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// TODO json parsing and error handling should happen in renderer process
|
|
19
|
+
export const get = async (path) => {
|
|
20
|
+
try {
|
|
21
|
+
const absolutePath = Path.join(path, 'extension.json')
|
|
22
|
+
const json = await ReadJson.readJson(absolutePath)
|
|
23
|
+
if (!isObject(json)) {
|
|
24
|
+
// TODO should include stack of extension json file here
|
|
25
|
+
throw new VError('Invalid manifest file: Not an JSON object.')
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
...json,
|
|
29
|
+
path,
|
|
30
|
+
status: ExtensionManifestStatus.Resolved,
|
|
31
|
+
}
|
|
32
|
+
} catch (error) {
|
|
33
|
+
const id = inferExtensionId(path)
|
|
34
|
+
const enhancedError = new VError(
|
|
35
|
+
error,
|
|
36
|
+
`Failed to load extension manifest for ${id}`
|
|
37
|
+
)
|
|
38
|
+
if (error.code === FileSystemErrorCodes.ENOENT) {
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
enhancedError.code = FileSystemErrorCodes.E_MANIFEST_NOT_FOUND
|
|
41
|
+
} else {
|
|
42
|
+
// @ts-ignore
|
|
43
|
+
enhancedError.code = error.code
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
path,
|
|
47
|
+
status: ExtensionManifestStatus.Rejected,
|
|
48
|
+
reason: enhancedError,
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as ExtensionManifestInputType from '../ExtensionManifestInputType/ExtensionManifestInputType.js'
|
|
2
|
+
import * as ExtensionManifestsGetFromFolder from './ExtensionManifestsFromFolder.js'
|
|
3
|
+
import * as ExtensionManifestsGetFromOnlyExtension from './ExtensionManifestsFromOnlyExtension.js'
|
|
4
|
+
|
|
5
|
+
const getModule = (type) => {
|
|
6
|
+
switch (type) {
|
|
7
|
+
case ExtensionManifestInputType.Folder:
|
|
8
|
+
return ExtensionManifestsGetFromFolder
|
|
9
|
+
case ExtensionManifestInputType.OnlyExtension:
|
|
10
|
+
return ExtensionManifestsGetFromOnlyExtension
|
|
11
|
+
default:
|
|
12
|
+
throw new Error('unsupported input type')
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const get = (input) => {
|
|
17
|
+
const module = getModule(input.type)
|
|
18
|
+
return module.getExtensionManifests(input.path)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const deduplicateExtensions = (extensions) => {
|
|
22
|
+
const seen = Object.create(null)
|
|
23
|
+
const uniqueExtensions = []
|
|
24
|
+
for (const extension of extensions) {
|
|
25
|
+
if (extension.id in seen) {
|
|
26
|
+
continue
|
|
27
|
+
}
|
|
28
|
+
seen[extension.id] = true
|
|
29
|
+
uniqueExtensions.push(extension)
|
|
30
|
+
}
|
|
31
|
+
return uniqueExtensions
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const getAll = async (inputs) => {
|
|
35
|
+
const manifests = await Promise.all(inputs.map(get))
|
|
36
|
+
const flatManifests = manifests.flat(1)
|
|
37
|
+
const uniqueExtensions = deduplicateExtensions(flatManifests)
|
|
38
|
+
return uniqueExtensions
|
|
39
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import VError from 'verror'
|
|
2
|
+
import * as ExtensionManifest from '../ExtensionManifest/ExtensionManifest.js'
|
|
3
|
+
import * as FileSystem from '../FileSystem/FileSystem.js'
|
|
4
|
+
import * as Path from '../Path/Path.js'
|
|
5
|
+
import * as FileSystemErrorCodes from '../FileSystemErrorCodes/FileSystemErrorCodes.js'
|
|
6
|
+
|
|
7
|
+
const toAbsolutePaths = (path, dirents) => {
|
|
8
|
+
const absolutePaths = []
|
|
9
|
+
for (const dirent of dirents) {
|
|
10
|
+
absolutePaths.push(Path.join(path, dirent))
|
|
11
|
+
}
|
|
12
|
+
return absolutePaths
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const getExtensionManifests = async (path) => {
|
|
16
|
+
try {
|
|
17
|
+
if (!path) {
|
|
18
|
+
return []
|
|
19
|
+
}
|
|
20
|
+
const dirents = await FileSystem.readDir(path)
|
|
21
|
+
const absolutePaths = toAbsolutePaths(path, dirents)
|
|
22
|
+
const manifests = await Promise.all(
|
|
23
|
+
absolutePaths.map(ExtensionManifest.get)
|
|
24
|
+
)
|
|
25
|
+
return manifests
|
|
26
|
+
} catch (error) {
|
|
27
|
+
// TODO how to make typescript happy?
|
|
28
|
+
// @ts-ignore
|
|
29
|
+
if (error.code === FileSystemErrorCodes.ENOENT) {
|
|
30
|
+
return []
|
|
31
|
+
}
|
|
32
|
+
throw new VError(error, 'Failed to get extension manifests')
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import VError from 'verror'
|
|
2
|
+
import * as FileSystem from '../FileSystem/FileSystem.js'
|
|
3
|
+
import * as Path from '../Path/Path.js'
|
|
4
|
+
import * as Platform from '../Platform/Platform.js'
|
|
5
|
+
|
|
6
|
+
export const unlink = async (path) => {
|
|
7
|
+
try {
|
|
8
|
+
const manifestPath = Path.join(path, 'extension.json')
|
|
9
|
+
if (!(await FileSystem.exists(manifestPath))) {
|
|
10
|
+
throw new Error('no extension manifest found')
|
|
11
|
+
}
|
|
12
|
+
const linkedExtensionsPath = Platform.getLinkedExtensionsPath()
|
|
13
|
+
const baseName = Path.basename(path)
|
|
14
|
+
const to = Path.join(linkedExtensionsPath, baseName)
|
|
15
|
+
await FileSystem.remove(to)
|
|
16
|
+
} catch (error) {
|
|
17
|
+
throw new VError(error, `Failed to unlink extension`)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -177,6 +177,18 @@ export const readDirWithFileTypes = async (path) => {
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
export const readDir = async (path) => {
|
|
181
|
+
try {
|
|
182
|
+
const dirents = await fs.readdir(path)
|
|
183
|
+
return dirents
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (error && error.code === FileSystemErrorCodes.ENOENT) {
|
|
186
|
+
throw new FileNotFoundError(path)
|
|
187
|
+
}
|
|
188
|
+
throw new VError(error, `Failed to read directory "${path}"`)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
180
192
|
export const mkdir = async (path) => {
|
|
181
193
|
try {
|
|
182
194
|
await fs.mkdir(path, { recursive: true })
|
|
@@ -38,6 +38,10 @@ export const getCachedExtensionsPath = () => {
|
|
|
38
38
|
return Path.join(cacheDir, 'cached-extensions')
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
export const getLinkedExtensionsPath = () => {
|
|
42
|
+
return Path.join(dataDir, 'linked-extensions')
|
|
43
|
+
}
|
|
44
|
+
|
|
41
45
|
export const getMarketplaceUrl = () => {
|
|
42
46
|
return (
|
|
43
47
|
process.env.LVCE_MARKETPLACE_URL ||
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { symlink } from 'node:fs/promises'
|
|
1
|
+
import { mkdir, symlink } from 'node:fs/promises'
|
|
2
|
+
import { dirname } from 'node:path'
|
|
2
3
|
import { FileSystemError } from '../Error/FileSystemError.js'
|
|
3
4
|
|
|
4
5
|
// TODO maybe move this to FileSystem module
|
|
5
6
|
|
|
6
7
|
export const createSymLink = async (target, path) => {
|
|
7
8
|
try {
|
|
9
|
+
await mkdir(dirname(path), { recursive: true })
|
|
8
10
|
await symlink(target, path)
|
|
9
11
|
} catch (error) {
|
|
10
12
|
throw new FileSystemError(
|
package/src/sharedProcessMain.js
CHANGED
|
@@ -45,7 +45,7 @@ const handleSigTerm = () => {
|
|
|
45
45
|
console.info('[shared-process] sigterm')
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
const knownCliArgs = ['install', 'list', 'link']
|
|
48
|
+
const knownCliArgs = ['install', 'list', 'link', 'unlink']
|
|
49
49
|
|
|
50
50
|
const main = async () => {
|
|
51
51
|
const argv = process.argv.slice(2)
|