@lvce-editor/shared-process 0.4.4 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/config/builtinCommands.json +15 -2
  2. package/config/defaultKeyBindings.json +33 -0
  3. package/config/defaultSettings.json +7 -3
  4. package/extensions/builtin.language-basics-docker/extension.json +1 -1
  5. package/extensions/builtin.language-basics-docker/src/tokenizeDockerfile.js +29 -11
  6. package/extensions/builtin.language-basics-editorconfig/src/tokenizeEditorConfig.js +62 -11
  7. package/extensions/builtin.language-basics-gitattributes/src/tokenizeGitAttributes.js +32 -5
  8. package/extensions/builtin.language-basics-html/src/tokenizeHtml.js +7 -2
  9. package/extensions/builtin.language-basics-javascript/extension.json +1 -1
  10. package/extensions/builtin.language-basics-javascript/src/tokenizeJavaScript.js +11 -7
  11. package/extensions/builtin.language-basics-json/extension.json +3 -2
  12. package/extensions/builtin.language-basics-json/src/tokenizeJson.js +11 -24
  13. package/extensions/builtin.language-basics-nvmrc/README.md +14 -0
  14. package/extensions/builtin.language-basics-nvmrc/extension.json +12 -0
  15. package/extensions/builtin.language-basics-nvmrc/src/tokenizeNvmrc.js +30 -0
  16. package/extensions/builtin.language-basics-plaintext/src/tokenizePlaintext.js +4 -7
  17. package/extensions/builtin.language-basics-rust/README.md +14 -0
  18. package/extensions/builtin.language-basics-rust/extension.json +12 -0
  19. package/extensions/builtin.language-basics-rust/src/tokenizeRust.js +158 -0
  20. package/extensions/builtin.language-basics-shellscript/src/tokenizeShellScript.js +37 -7
  21. package/extensions/builtin.language-basics-toml/extension.json +1 -1
  22. package/extensions/builtin.language-basics-toml/src/tokenizeToml.js +93 -8
  23. package/extensions/builtin.language-basics-yaml/src/tokenizeYaml.js +138 -10
  24. package/extensions/builtin.vscode-icons/icon-theme.json +14 -0
  25. package/extensions/builtin.vscode-icons/icons/file_type_master-co.svg +1 -0
  26. package/extensions/builtin.vscode-icons/icons/file_type_wgsl.svg +86 -0
  27. package/package.json +8 -7
  28. package/src/parts/Cli/Cli.js +24 -0
  29. package/src/parts/CliInstall/CliInstall.js +10 -0
  30. package/src/parts/CliLink/CliLink.js +6 -0
  31. package/src/parts/CliList/CliList.js +15 -0
  32. package/src/parts/Download/Download.js +5 -13
  33. package/src/parts/DownloadAndExtract/DownloadAndExtract.js +14 -0
  34. package/src/parts/Error/FileSystemError.js +10 -0
  35. package/src/parts/ExtensionInstall/ExtensionInstall.ipc.js +6 -0
  36. package/src/parts/ExtensionInstall/ExtensionInstall.js +25 -0
  37. package/src/parts/ExtensionInstallFromGitHub/ExtensionInstallFromGitHub.js +34 -0
  38. package/src/parts/ExtensionInstallFromUrl/ExtensionInstallFromUrl.js +32 -0
  39. package/src/parts/ExtensionInstallParseInput/ExtensionInstallParseInput.js +84 -0
  40. package/src/parts/ExtensionLink/ExtensionLink.js +32 -0
  41. package/src/parts/ExtensionList/ExtensionList.js +66 -0
  42. package/src/parts/ExtensionManagement/ExtensionManagement.js +4 -3
  43. package/src/parts/ExtensionUninstall/ExtensionUninstall.js +0 -0
  44. package/src/parts/Extract/Extract.js +34 -0
  45. package/src/parts/FileSystem/FileSystem.js +21 -2
  46. package/src/parts/FileSystemErrorCodes/FileSystemErrorCodes.js +4 -0
  47. package/src/parts/Path/Path.js +4 -0
  48. package/src/parts/Preferences/Preferences.js +17 -1
  49. package/src/parts/SymLink/SymLink.js +15 -0
  50. package/src/parts/TmpFile/TmpFile.js +15 -0
  51. package/src/sharedProcessMain.js +16 -5
@@ -667,6 +667,7 @@
667
667
  "_f_markdownlint_ignore": "/icons/file_type_markdownlint_ignore.svg",
668
668
  "_f_marko": "/icons/file_type_marko.svg",
669
669
  "_f_markojs": "/icons/file_type_markojs.svg",
670
+ "_f_master-co": "/icons/file_type_master-co.svg",
670
671
  "_f_matlab": "/icons/file_type_matlab.svg",
671
672
  "_f_maven": "/icons/file_type_maven.svg",
672
673
  "_f_maxscript": "/icons/file_type_maxscript.svg",
@@ -994,6 +995,7 @@
994
995
  "_f_webpack": "/icons/file_type_webpack.svg",
995
996
  "_f_wenyan": "/icons/file_type_wenyan.svg",
996
997
  "_f_wercker": "/icons/file_type_wercker.svg",
998
+ "_f_wgsl": "/icons/file_type_wgsl.svg",
997
999
  "_f_wikitext": "/icons/file_type_wikitext.svg",
998
1000
  "_f_windi": "/icons/file_type_windi.svg",
999
1001
  "_f_wolfram": "/icons/file_type_wolfram.svg",
@@ -1195,6 +1197,7 @@
1195
1197
  ".eslintrc": "_f_eslint",
1196
1198
  ".eslintignore": "_f_eslint",
1197
1199
  ".eslintcache": "_f_eslint",
1200
+ "eslint.config.js": "_f_eslint",
1198
1201
  ".eslintrc.js": "_f_eslint",
1199
1202
  ".eslintrc.mjs": "_f_eslint",
1200
1203
  ".eslintrc.cjs": "_f_eslint",
@@ -1377,6 +1380,7 @@
1377
1380
  "license-apache.md": "_f_license",
1378
1381
  "license-apache.txt": "_f_license",
1379
1382
  ".licrc": "_f_licensebat",
1383
+ ".lighthouserc.cjs": "_f_lighthouse",
1380
1384
  ".lighthouserc.js": "_f_lighthouse",
1381
1385
  ".lighthouserc.json": "_f_lighthouse",
1382
1386
  ".lighthouserc.yaml": "_f_lighthouse",
@@ -1407,6 +1411,14 @@
1407
1411
  ".markdownlint-cli2.yaml": "_f_markdownlint",
1408
1412
  ".markdownlint-cli2.cjs": "_f_markdownlint",
1409
1413
  ".markdownlintignore": "_f_markdownlint_ignore",
1414
+ "master.js": "_f_master-co",
1415
+ "master.ts": "_f_master-co",
1416
+ "master.mjs": "_f_master-co",
1417
+ "master.json": "_f_master-co",
1418
+ "master.css.js": "_f_master-co",
1419
+ "master.css.ts": "_f_master-co",
1420
+ "master.css.mjs": "_f_master-co",
1421
+ "master.css.json": "_f_master-co",
1410
1422
  "maven.config": "_f_maven",
1411
1423
  "pom.xml": "_f_maven",
1412
1424
  "extensions.xml": "_f_maven",
@@ -1437,6 +1449,7 @@
1437
1449
  "ng-tailwind.js": "_f_ng_tailwind",
1438
1450
  "nginx.conf": "_f_nginx",
1439
1451
  "build.ninja": "_f_ninja",
1452
+ "flake.lock": "_f_nix",
1440
1453
  ".node-version": "_f_node",
1441
1454
  ".nvmrc": "_f_node",
1442
1455
  "nodemon.json": "_f_nodemon",
@@ -3520,6 +3533,7 @@
3520
3533
  "wasm": "_f_wasm",
3521
3534
  "wat": "_f_wasm",
3522
3535
  "wenyan": "_f_wenyan",
3536
+ "wgsl": "_f_wgsl",
3523
3537
  "wikitext": "_f_wikitext",
3524
3538
  "wolfram": "_f_wolfram",
3525
3539
  "wurstlang": "_f_wurst",
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32"><defs><linearGradient id="a" x1="0.5" y1="-1.054" x2="0.5" y2="1.002" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#c8861c"/><stop offset="0.323" stop-color="#e6a345"/><stop offset="1" stop-color="#f6db9b"/></linearGradient><linearGradient id="b" x1="21.397" y1="-1.054" x2="21.397" y2="1.002" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#c78a28"/><stop offset="0.323" stop-color="#e6a345"/><stop offset="1" stop-color="#f6db9b"/></linearGradient><linearGradient id="c" x1="0.156" y1="-0.44" x2="0.754" y2="1.009" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#fcdd98"/><stop offset="0.232" stop-color="#fada94"/><stop offset="0.407" stop-color="#f6d48b"/><stop offset="0.564" stop-color="#efc87b"/><stop offset="0.709" stop-color="#e6b864"/><stop offset="0.847" stop-color="#d9a346"/><stop offset="0.975" stop-color="#c98922"/><stop offset="1" stop-color="#e6a345"/></linearGradient><clipPath id="e"><rect width="32" height="32"/></clipPath></defs><g id="d" clip-path="url(#e)"><g transform="translate(1.999 8.399)"><path d="M581.955,173.456c-.732-1.3-1.349-1.489-1.949-1.489-.882,0-1.495.815-1.887,1.539l-1.564,3.212.426.7a5.836,5.836,0,0,0,7.852,2.464l.407-.212Z" transform="translate(-557.239 -165.337)" fill="url(#a)"/><path d="M519.451,173.456c.732-1.3,1.348-1.489,1.95-1.489.882,0,1.494.815,1.886,1.539l1.564,3.212-.426.7a5.836,5.836,0,0,1-7.852,2.464l-.407-.212Z" transform="translate(-516.166 -165.337)" fill="url(#b)"/><path d="M546.924,152.989a3.231,3.231,0,0,0-5.725-.035l-1.686,3.23-1.687-3.23a3.231,3.231,0,0,0-5.725.035l-2.194,4.245c1.042-1.182,2.5-.486,3.137.717l2.366,4.356,1.21,2.315a3.208,3.208,0,0,0,1.378,1.4,3.349,3.349,0,0,0,3.026,0,3.209,3.209,0,0,0,1.378-1.4l1.21-2.315,2.366-4.356c.636-1.2,2.1-1.9,3.137-.717Z" transform="translate(-525.512 -151.239)" fill="url(#c)"/></g></g></svg>
@@ -0,0 +1,86 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ id="Logo"
4
+ viewBox="0 0 768 600"
5
+ width="32"
6
+ height="32"
7
+ version="1.1"
8
+ sodipodi:docname="file_type_wgsl.svg"
9
+ inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
10
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
12
+ xmlns="http://www.w3.org/2000/svg"
13
+ xmlns:svg="http://www.w3.org/2000/svg">
14
+ <sodipodi:namedview
15
+ id="namedview63"
16
+ pagecolor="#ffffff"
17
+ bordercolor="#666666"
18
+ borderopacity="1.0"
19
+ inkscape:pageshadow="2"
20
+ inkscape:pageopacity="0.0"
21
+ inkscape:pagecheckerboard="0"
22
+ showgrid="false"
23
+ inkscape:zoom="25.588427"
24
+ inkscape:cx="8.871198"
25
+ inkscape:cy="17.664236"
26
+ inkscape:window-width="2520"
27
+ inkscape:window-height="1365"
28
+ inkscape:window-x="15"
29
+ inkscape:window-y="50"
30
+ inkscape:window-maximized="1"
31
+ inkscape:current-layer="Logo" />
32
+ <defs
33
+ id="defs55">
34
+ <style
35
+ id="style53">
36
+ .cls-1 {
37
+ fill: #005a9c;
38
+ }
39
+
40
+ .cls-1, .cls-2, .cls-3, .cls-4, .cls-5, .cls-6 {
41
+ fill-rule: evenodd;
42
+ }
43
+
44
+ .cls-2 {
45
+ fill: #0066b0;
46
+ }
47
+
48
+ .cls-3 {
49
+ fill: #0076cc;
50
+ }
51
+
52
+ .cls-4 {
53
+ fill: #0086e8;
54
+ }
55
+
56
+ .cls-5 {
57
+ fill: #0093ff;
58
+ }
59
+ </style>
60
+ </defs>
61
+ <path
62
+ id="Triangle_1"
63
+ data-name="Triangle 1"
64
+ class="cls-1"
65
+ d="M 293.6275,508.5 52.8725,91.5 h 481.51 z" />
66
+ <path
67
+ id="Triangle_2"
68
+ data-name="Triangle 2"
69
+ class="cls-2"
70
+ d="m 534.6275,91.5 -120.5,208 h 241 z" />
71
+ <path
72
+ id="Triangle_3"
73
+ data-name="Triangle 3"
74
+ class="cls-3"
75
+ d="m 534.6275,507.5 -120.5,-208 h 241 z" />
76
+ <path
77
+ id="Triangle_4"
78
+ data-name="Triangle 4"
79
+ class="cls-4"
80
+ d="m 654.6275,300.5 -60.5,-104 h 121 z" />
81
+ <path
82
+ id="Triangle_5"
83
+ data-name="Triangle 5"
84
+ class="cls-5"
85
+ d="m 654.6275,92.5 -60.5,104 h 121 z" />
86
+ </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/shared-process",
3
- "version": "0.4.4",
3
+ "version": "0.6.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -20,11 +20,13 @@
20
20
  "node": ">=16"
21
21
  },
22
22
  "dependencies": {
23
+ "@lvce-editor/extension-host": "0.6.0",
24
+ "@lvce-editor/pty-host": "0.6.0",
23
25
  "chokidar": "^3.5.3",
24
26
  "debug": "^4.3.4",
25
27
  "execa": "^6.1.0",
26
28
  "exit-hook": "^3.1.0",
27
- "got": "^12.4.1",
29
+ "got": "^12.5.0",
28
30
  "is-object": "^1.0.2",
29
31
  "json-parse-even-better-errors": "^2.3.1",
30
32
  "jsonc-parser": "^3.2.0",
@@ -34,17 +36,16 @@
34
36
  "parse-json": "^6.0.2",
35
37
  "tail": "^2.2.4",
36
38
  "tar-fs": "^2.1.1",
39
+ "tmp-promise": "^3.0.3",
37
40
  "trash": "^8.1.0",
38
41
  "verror": "^1.10.1",
39
- "ws": "^8.8.1",
40
- "xdg-basedir": "^5.1.0",
41
- "@lvce-editor/pty-host": "0.4.4",
42
- "@lvce-editor/extension-host": "0.4.4"
42
+ "ws": "^8.9.0",
43
+ "xdg-basedir": "^5.1.0"
43
44
  },
44
45
  "optionalDependencies": {
45
46
  "electron-clipboard-ex": "^1.3.3",
46
47
  "keytar": "^7.9.0",
47
- "vscode-ripgrep-with-github-api-error-fix": "^2.4.1"
48
+ "vscode-ripgrep-with-github-api-error-fix": "^2.5.0"
48
49
  },
49
50
  "devDependencies": {
50
51
  "@types/debug": "^4.1.7",
@@ -0,0 +1,24 @@
1
+ const getModule = (argv0) => {
2
+ switch (argv0) {
3
+ case 'install':
4
+ return import('../CliInstall/CliInstall.js')
5
+ case 'list':
6
+ return import('../CliList/CliList.js')
7
+ case 'link':
8
+ return import('../CliLink/CliLink.js')
9
+ default:
10
+ throw new Error('command not found')
11
+ }
12
+ }
13
+
14
+ export const handleCliArgs = async (argv, console, process) => {
15
+ const argv0 = argv[0]
16
+ const module = await getModule(argv0)
17
+ // console.log('handle cli args')
18
+ try {
19
+ await module.handleCliArgs(argv, console, process)
20
+ } catch (error) {
21
+ console.error(error)
22
+ process.exitCode = 1
23
+ }
24
+ }
@@ -0,0 +1,10 @@
1
+ import * as ExtensionInstall from '../ExtensionInstall/ExtensionInstall.js'
2
+
3
+ export const handleCliArgs = async (argv, console) => {
4
+ const extension = argv[1]
5
+ if (!extension) {
6
+ console.error('extension argument is required')
7
+ return
8
+ }
9
+ await ExtensionInstall.install(extension)
10
+ }
@@ -0,0 +1,6 @@
1
+ import * as ExtensionLink from '../ExtensionLink/ExtensionLink.js'
2
+
3
+ export const handleCliArgs = async (argv, console) => {
4
+ const path = argv.length > 1 ? argv[1] : process.cwd()
5
+ await ExtensionLink.link(path)
6
+ }
@@ -0,0 +1,15 @@
1
+ import * as ExtensionList from '../ExtensionList/ExtensionList.js'
2
+
3
+ const getOutputLine = (extension) => {
4
+ return `${extension.id}: ${extension.version}`
5
+ }
6
+
7
+ const getOutput = (extensions) => {
8
+ return extensions.map(getOutputLine).join('\n')
9
+ }
10
+
11
+ export const handleCliArgs = async (argv, console) => {
12
+ const extensions = await ExtensionList.list()
13
+ const output = getOutput(extensions)
14
+ console.info(output)
15
+ }
@@ -1,9 +1,7 @@
1
- import got from 'got'
2
- import { createReadStream, createWriteStream } from 'node:fs'
1
+ import got, { RequestError } from 'got'
2
+ import { createWriteStream } from 'node:fs'
3
3
  import { mkdir, rm } from 'node:fs/promises'
4
4
  import { pipeline } from 'node:stream/promises'
5
- import { createBrotliDecompress } from 'node:zlib'
6
- import tar from 'tar-fs'
7
5
  import VError from 'verror'
8
6
  import * as Path from '../Path/Path.js'
9
7
 
@@ -17,15 +15,9 @@ export const download = async (url, outFile) => {
17
15
  } catch {
18
16
  // ignore
19
17
  }
18
+ if (error instanceof RequestError) {
19
+ throw new VError(`Failed to download "${url}": ${error.message}`)
20
+ }
20
21
  throw new VError(error, `Failed to download "${url}"`)
21
22
  }
22
23
  }
23
-
24
- export const extract = async (inFile, outDir) => {
25
- await mkdir(outDir, { recursive: true })
26
- await pipeline(
27
- createReadStream(inFile),
28
- createBrotliDecompress(),
29
- tar.extract(outDir)
30
- )
31
- }
@@ -0,0 +1,14 @@
1
+ import got from 'got'
2
+ import { pipeline } from 'stream/promises'
3
+ import tar from 'tar-fs'
4
+ import { createGunzip } from 'zlib'
5
+
6
+ export const downloadAndExtractTarGz = async ({ url, outDir, strip }) => {
7
+ await pipeline(
8
+ got.stream(url),
9
+ createGunzip(),
10
+ tar.extract(outDir, {
11
+ strip,
12
+ })
13
+ )
14
+ }
@@ -0,0 +1,10 @@
1
+ import VError from 'verror'
2
+
3
+ export class FileSystemError extends VError {
4
+ constructor(error, message) {
5
+ super(error, message)
6
+ if (error && error.code) {
7
+ this.code = error.code
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,6 @@
1
+ import * as ExensionInstall from './ExtensionInstall.js'
2
+
3
+ // prettier-ignore
4
+ export const Commands = {
5
+ 'ExtensionInstall.install': ExensionInstall.install,
6
+ }
@@ -0,0 +1,25 @@
1
+ import * as ExtensionInstallParseInput from '../ExtensionInstallParseInput/ExtensionInstallParseInput.js'
2
+
3
+ const getModule = (type) => {
4
+ switch (type) {
5
+ case ExtensionInstallParseInput.InstallType.GithubRepository:
6
+ return import(
7
+ '../ExtensionInstallFromGitHub/ExtensionInstallFromGitHub.js'
8
+ )
9
+ case ExtensionInstallParseInput.InstallType.Url:
10
+ return import('../ExtensionInstallFromUrl/ExtensionInstallFromUrl.js')
11
+ default:
12
+ throw new Error('module not found')
13
+ }
14
+ }
15
+
16
+ export const install = async (input) => {
17
+ const parsed = ExtensionInstallParseInput.parse(input)
18
+ if (parsed.type === ExtensionInstallParseInput.InstallType.ParsingError) {
19
+ throw new Error(`Failed to parse input: ${parsed.options.message}`)
20
+ }
21
+ console.log({ parsed })
22
+ const module = await getModule(parsed.type)
23
+ // @ts-ignore
24
+ await module.install(parsed.options)
25
+ }
@@ -0,0 +1,34 @@
1
+ import VError from 'verror'
2
+ import * as DownloadAndExtract from '../DownloadAndExtract/DownloadAndExtract.js'
3
+ import * as FileSystem from '../FileSystem/FileSystem.js'
4
+ import * as Path from '../Path/Path.js'
5
+ import * as Platform from '../Platform/Platform.js'
6
+
7
+ export const install = async ({ user, repo, branch }) => {
8
+ try {
9
+ const cachedExtensionsPath = Platform.getCachedExtensionsPath()
10
+ const url = `https://codeload.github.com/${user}/${repo}/tar.gz/${branch}`
11
+ const cachedExtensionPath = Path.join(
12
+ cachedExtensionsPath,
13
+ `github-${user}-${repo}-${branch}`
14
+ )
15
+ await DownloadAndExtract.downloadAndExtractTarGz({
16
+ url,
17
+ outDir: cachedExtensionPath,
18
+ strip: 1,
19
+ })
20
+ const extensionsPath = Platform.getExtensionsPath()
21
+ const manifestPath = Path.join(cachedExtensionPath, 'extension.json')
22
+ const manifestContent = await FileSystem.readFile(manifestPath)
23
+ const manifestJson = JSON.parse(manifestContent)
24
+ const id = manifestJson.id
25
+ if (!id) {
26
+ throw new Error('missing id in extension manifest')
27
+ }
28
+ const outDir = Path.join(extensionsPath, id)
29
+ await FileSystem.remove(outDir)
30
+ await FileSystem.rename(cachedExtensionPath, outDir)
31
+ } catch (error) {
32
+ throw new VError(error, `Failed to install ${user}/${repo}`)
33
+ }
34
+ }
@@ -0,0 +1,32 @@
1
+ import { readFile, rename, rm } from 'fs/promises'
2
+ import { join } from 'path'
3
+ import VError from 'verror'
4
+ import * as Download from '../Download/Download.js'
5
+ import * as Extract from '../Extract/Extract.js'
6
+ import * as Platform from '../Platform/Platform.js'
7
+ import * as TmpFile from '../TmpFile/TmpFile.js'
8
+
9
+ export const install = async ({ url }) => {
10
+ try {
11
+ // TODO cache extension tar br file, probably by url <cachedExtensions>/user/repo/releases/<tag>/<fileName>
12
+ // const cachedExtensionsPath = Platform.getCachedExtensionsPath()
13
+ // const outFile = join(cachedExtensionsPath, 'installed-extension.tar.br')
14
+ const extensionsPath = Platform.getExtensionsPath()
15
+ const tmpFile = await TmpFile.getTmpFile()
16
+ await Download.download(url, tmpFile)
17
+ const tmpDir = await TmpFile.getTmpDir()
18
+ await Extract.extractTarBr(tmpFile, tmpDir)
19
+ const manifestPath = join(tmpDir, 'extension.json')
20
+ const manifestContent = await readFile(manifestPath, 'utf8')
21
+ const manifestJson = JSON.parse(manifestContent)
22
+ const id = manifestJson.id
23
+ if (!id) {
24
+ throw new Error('missing id in extension manifest')
25
+ }
26
+ const outDir = join(extensionsPath, id)
27
+ await rm(outDir, { recursive: true, force: true })
28
+ await rename(tmpDir, outDir)
29
+ } catch (error) {
30
+ throw new VError(error, `Failed to install "${url}"`)
31
+ }
32
+ }
@@ -0,0 +1,84 @@
1
+ export const InstallType = {
2
+ Url: 1,
3
+ GithubRepository: 2,
4
+ ParsingError: 3,
5
+ }
6
+
7
+ const parseUrlGithub = (input) => {
8
+ const parts = input.split('/')
9
+ const slashCount = parts.length
10
+ if (slashCount === 5) {
11
+ const user = parts[3]
12
+ const repo = parts[4]
13
+ return {
14
+ type: InstallType.GithubRepository,
15
+ options: {
16
+ user,
17
+ repo,
18
+ branch: 'HEAD',
19
+ },
20
+ }
21
+ }
22
+ if (slashCount === 7) {
23
+ const user = parts[3]
24
+ const repo = parts[4]
25
+ const type = parts[5]
26
+ const commit = parts[6]
27
+ if (type === 'tree') {
28
+ return {
29
+ type: InstallType.GithubRepository,
30
+ options: {
31
+ user,
32
+ repo,
33
+ branch: commit,
34
+ },
35
+ }
36
+ }
37
+ if (type === 'pull') {
38
+ return {
39
+ type: InstallType.ParsingError,
40
+ options: {
41
+ message: 'Cannot download from Pull Request',
42
+ },
43
+ }
44
+ }
45
+ }
46
+ return {
47
+ type: InstallType.ParsingError,
48
+ options: {
49
+ message: 'Failed to parse github url',
50
+ },
51
+ }
52
+ }
53
+
54
+ const parseUrl = (input) => {
55
+ if (input.startsWith('https://github.com')) {
56
+ return parseUrlGithub(input)
57
+ }
58
+ if (input.endsWith('.tar.br')) {
59
+ return {
60
+ type: InstallType.Url,
61
+ options: {
62
+ url: input,
63
+ },
64
+ }
65
+ }
66
+ return {
67
+ type: InstallType.ParsingError,
68
+ options: {
69
+ message: 'Failed to parse url',
70
+ },
71
+ }
72
+ }
73
+
74
+ export const parse = (input) => {
75
+ if (input.startsWith('https://')) {
76
+ return parseUrl(input)
77
+ }
78
+ return {
79
+ type: InstallType.ParsingError,
80
+ options: {
81
+ message: 'Failed to parse input',
82
+ },
83
+ }
84
+ }
@@ -0,0 +1,32 @@
1
+ import VError from 'verror'
2
+ import * as FileSystem from '../FileSystem/FileSystem.js'
3
+ import * as FileSystemErrorCodes from '../FileSystemErrorCodes/FileSystemErrorCodes.js'
4
+ import * as Path from '../Path/Path.js'
5
+ import * as Platform from '../Platform/Platform.js'
6
+ import * as SymLink from '../SymLink/SymLink.js'
7
+
8
+ const linkFallBack = async (path) => {
9
+ try {
10
+ const extensionsPath = Platform.getExtensionsPath()
11
+ const baseName = Path.basename(path)
12
+ const to = Path.join(extensionsPath, baseName)
13
+ await FileSystem.remove(to)
14
+ await SymLink.createSymLink(path, to)
15
+ } catch (error) {
16
+ throw new VError(error, `Failed to link extension`)
17
+ }
18
+ }
19
+
20
+ export const link = async (path) => {
21
+ try {
22
+ const extensionsPath = Platform.getExtensionsPath()
23
+ const baseName = Path.basename(path)
24
+ const to = Path.join(extensionsPath, baseName)
25
+ await SymLink.createSymLink(path, to)
26
+ } catch (error) {
27
+ if (error && error.code === FileSystemErrorCodes.EEXIST) {
28
+ return linkFallBack(path)
29
+ }
30
+ throw new VError(error, `Failed to link extension`)
31
+ }
32
+ }
@@ -0,0 +1,66 @@
1
+ import { readdir, readFile } from 'node:fs/promises'
2
+ import { basename, dirname, join } from 'node:path'
3
+ import VError from 'verror'
4
+ import * as Platform from '../Platform/Platform.js'
5
+ import * as JsonFile from '../JsonFile/JsonFile.js'
6
+
7
+ const extensionPath = Platform.getExtensionsPath()
8
+
9
+ const getAbsolutePath = (dirent) => {
10
+ return join(extensionPath, dirent)
11
+ }
12
+
13
+ const getManifestVersion = (json) => {
14
+ if (json && json.version && typeof json.version === 'string') {
15
+ return json.version
16
+ }
17
+ return 'n/a'
18
+ }
19
+
20
+ const getManifestId = (json, jsonPath) => {
21
+ if (json && json.id && typeof json.id === 'string') {
22
+ return json.id
23
+ }
24
+ return basename(jsonPath)
25
+ }
26
+
27
+ const getManifestInfo = async (extensionPath) => {
28
+ const manifestPath = join(extensionPath, 'extension.json')
29
+ const json = await JsonFile.readJson(manifestPath)
30
+ const id = getManifestId(json, extensionPath)
31
+ const version = getManifestVersion(json)
32
+ return {
33
+ id,
34
+ version,
35
+ }
36
+ }
37
+
38
+ const isFulfilled = (result) => {
39
+ return result.status === 'fulfilled'
40
+ }
41
+
42
+ const getValue = (result) => {
43
+ return result.value
44
+ }
45
+
46
+ const getManifestInfos = async (manifestPaths) => {
47
+ const manifestInfos = await Promise.allSettled(
48
+ manifestPaths.map(getManifestInfo)
49
+ )
50
+ const results = manifestInfos.filter(isFulfilled).map(getValue)
51
+ return results
52
+ }
53
+
54
+ export const list = async () => {
55
+ try {
56
+ const dirents = await readdir(extensionPath)
57
+ const extensionPaths = dirents.map(getAbsolutePath)
58
+ const infos = await getManifestInfos(extensionPaths)
59
+ return infos
60
+ } catch (error) {
61
+ if (error && error.code === 'ENOENT') {
62
+ return []
63
+ }
64
+ throw new VError(error, `Failed to list extensions`)
65
+ }
66
+ }
@@ -36,7 +36,8 @@ export const install = async (id) => {
36
36
  // TODO this should be a stateless function, renderer-worker should have info on marketplace url
37
37
  // TODO use command.execute
38
38
  try {
39
- const { download, extract } = await import('../Download/Download.js')
39
+ const { download } = await import('../Download/Download.js')
40
+ const { extractTarBr } = await import('../Extract/Extract.js')
40
41
  const marketplaceUrl = Platform.getMarketplaceUrl()
41
42
  Debug.debug(`ExtensionManagement#install ${id}`)
42
43
  const cachedExtensionsPath = Platform.getCachedExtensionsPath()
@@ -50,7 +51,7 @@ export const install = async (id) => {
50
51
  Path.join(cachedExtensionsPath, `${id}.tar.br`)
51
52
  )
52
53
  Debug.debug(`ExtensionManagement#extract ${id}`)
53
- await extract(
54
+ await extractTarBr(
54
55
  Path.join(cachedExtensionsPath, `${id}.tar.br`),
55
56
  Path.join(extensionsPath, id)
56
57
  )
@@ -258,7 +259,7 @@ export const getExtensions = async () => {
258
259
  return [...builtinExtensions, ...installedExtensions]
259
260
  }
260
261
 
261
- const RE_THEME = /[a-z]+\.[a-z\-]*theme-[a-z\d\-]+$/
262
+ const RE_THEME = /theme-[a-z\d\-]+$/
262
263
  const isTheme = (extensionId) => {
263
264
  return RE_THEME.test(extensionId)
264
265
  }
@@ -0,0 +1,34 @@
1
+ import { createReadStream } from 'node:fs'
2
+ import { mkdir } from 'node:fs/promises'
3
+ import { pipeline } from 'node:stream/promises'
4
+ import { createBrotliDecompress, createGunzip } from 'node:zlib'
5
+ import tar from 'tar-fs'
6
+ import VError from 'verror'
7
+
8
+ export const extractTarBr = async (inFile, outDir) => {
9
+ try {
10
+ await mkdir(outDir, { recursive: true })
11
+ await pipeline(
12
+ createReadStream(inFile),
13
+ createBrotliDecompress(),
14
+ tar.extract(outDir)
15
+ )
16
+ } catch (error) {
17
+ throw new VError(error, `Failed to extract ${inFile}`)
18
+ }
19
+ }
20
+
21
+ export const extractTarGz = async ({ inFile, outDir, strip }) => {
22
+ try {
23
+ await mkdir(outDir, { recursive: true })
24
+ await pipeline(
25
+ createReadStream(inFile),
26
+ createGunzip(),
27
+ tar.extract(outDir, {
28
+ strip,
29
+ })
30
+ )
31
+ } catch (error) {
32
+ throw new VError(error, `Failed to extract ${inFile}`)
33
+ }
34
+ }