@lvce-editor/shared-process 0.16.19 → 0.16.20
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/extensions/builtin.language-basics-javascript/src/tokenizeJavaScript.js +19 -1
- package/extensions/builtin.language-basics-wgsl/README.md +16 -0
- package/extensions/builtin.language-basics-wgsl/extension.json +13 -0
- package/extensions/builtin.language-basics-wgsl/languageConfiguration.json +21 -0
- package/extensions/builtin.language-basics-wgsl/src/tokenizeWgsl.js +226 -0
- package/package.json +4 -4
- package/src/parts/ExtensionUnlink/ExtensionUnlink.js +2 -2
- package/src/parts/Platform/Platform.js +3 -3
- package/src/parts/RemoveSymlink/RemoveSymlink.js +12 -0
|
@@ -183,11 +183,13 @@ export const tokenizeLine = (line, lineState) => {
|
|
|
183
183
|
case 'null':
|
|
184
184
|
case 'undefined':
|
|
185
185
|
token = TokenType.LanguageConstant
|
|
186
|
+
state = State.TopLevelContent
|
|
186
187
|
break
|
|
187
188
|
case 'import':
|
|
188
189
|
case 'export':
|
|
189
190
|
case 'from':
|
|
190
191
|
token = TokenType.KeywordImport
|
|
192
|
+
state = State.TopLevelContent
|
|
191
193
|
break
|
|
192
194
|
case 'as':
|
|
193
195
|
case 'switch':
|
|
@@ -204,6 +206,7 @@ export const tokenizeLine = (line, lineState) => {
|
|
|
204
206
|
case 'continue':
|
|
205
207
|
case 'while':
|
|
206
208
|
token = TokenType.KeywordControl
|
|
209
|
+
state = State.TopLevelContent
|
|
207
210
|
break
|
|
208
211
|
case 'assert':
|
|
209
212
|
if (line.includes('import')) {
|
|
@@ -211,28 +214,35 @@ export const tokenizeLine = (line, lineState) => {
|
|
|
211
214
|
} else {
|
|
212
215
|
token = TokenType.FunctionName
|
|
213
216
|
}
|
|
217
|
+
state = State.TopLevelContent
|
|
214
218
|
break
|
|
215
219
|
case 'async':
|
|
216
220
|
case 'await':
|
|
217
221
|
token = TokenType.KeywordModifier
|
|
222
|
+
state = State.TopLevelContent
|
|
218
223
|
break
|
|
219
224
|
case 'return':
|
|
220
225
|
token = TokenType.KeywordReturn
|
|
226
|
+
state = State.TopLevelContent
|
|
221
227
|
break
|
|
222
228
|
case 'new':
|
|
223
229
|
token = TokenType.KeywordNew
|
|
230
|
+
state = State.TopLevelContent
|
|
224
231
|
break
|
|
225
232
|
case 'this':
|
|
226
233
|
token = TokenType.KeywordThis
|
|
234
|
+
state = State.TopLevelContent
|
|
227
235
|
break
|
|
228
236
|
case 'delete':
|
|
229
237
|
case 'typeof':
|
|
230
238
|
case 'in':
|
|
231
239
|
case 'instanceof':
|
|
232
240
|
token = TokenType.KeywordOperator
|
|
241
|
+
state = State.TopLevelContent
|
|
233
242
|
break
|
|
234
243
|
case 'function':
|
|
235
244
|
token = TokenType.KeywordFunction
|
|
245
|
+
state = State.TopLevelContent
|
|
236
246
|
break
|
|
237
247
|
case 'Infinity':
|
|
238
248
|
token = TokenType.Numeric
|
|
@@ -240,16 +250,18 @@ export const tokenizeLine = (line, lineState) => {
|
|
|
240
250
|
break
|
|
241
251
|
case 'of':
|
|
242
252
|
token = TokenType.KeywordOperator
|
|
253
|
+
state = State.TopLevelContent
|
|
243
254
|
break
|
|
244
255
|
case 'class':
|
|
256
|
+
case 'extends':
|
|
245
257
|
token = TokenType.Keyword
|
|
246
258
|
state = State.AfterKeywordClass
|
|
247
259
|
break
|
|
248
260
|
default:
|
|
249
261
|
token = TokenType.Keyword
|
|
262
|
+
state = State.TopLevelContent
|
|
250
263
|
break
|
|
251
264
|
}
|
|
252
|
-
state = State.TopLevelContent
|
|
253
265
|
} else if ((next = part.match(RE_FUNCTION_CALL_NAME))) {
|
|
254
266
|
token = TokenType.FunctionName
|
|
255
267
|
state = State.TopLevelContent
|
|
@@ -456,6 +468,12 @@ export const tokenizeLine = (line, lineState) => {
|
|
|
456
468
|
} else if ((next = part.match(RE_CURLY_OPEN))) {
|
|
457
469
|
token = TokenType.Punctuation
|
|
458
470
|
state = State.TopLevelContent
|
|
471
|
+
} else if ((next = part.match(RE_PUNCTUATION))) {
|
|
472
|
+
token = TokenType.Punctuation
|
|
473
|
+
state = State.TopLevelContent
|
|
474
|
+
} else if ((next = part.match(RE_ANYTHING))) {
|
|
475
|
+
token = TokenType.Text
|
|
476
|
+
state = State.TopLevelContent
|
|
459
477
|
} else {
|
|
460
478
|
throw new Error('no')
|
|
461
479
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Language Basics Wgsl
|
|
2
|
+
|
|
3
|
+
Wgsl syntax highlighting for Lvce Editor.
|
|
4
|
+
|
|
5
|
+
## Contributing
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
git clone git@github.com:lvce-editor/language-basics-wgsl.git &&
|
|
9
|
+
cd language-basics-wgsl &&
|
|
10
|
+
npm ci &&
|
|
11
|
+
npm test
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Gitpod
|
|
15
|
+
|
|
16
|
+
[](https://gitpod.io/#https://github.com/lvce-editor/language-basics-wgsl)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "builtin.language-basics-wgsl",
|
|
3
|
+
"name": "Language Basics WGSL",
|
|
4
|
+
"description": "Provides syntax highlighting and bracket matching in WebGPU Shader Language files.",
|
|
5
|
+
"languages": [
|
|
6
|
+
{
|
|
7
|
+
"id": "wgsl",
|
|
8
|
+
"extensions": [".wgsl"],
|
|
9
|
+
"tokenize": "src/tokenizeWgsl.js",
|
|
10
|
+
"configuration": "languageConfiguration.json"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"comments": {
|
|
3
|
+
"blockComment": ["/*", "*/"]
|
|
4
|
+
},
|
|
5
|
+
"brackets": [
|
|
6
|
+
["{", "}"],
|
|
7
|
+
["[", "]"],
|
|
8
|
+
["(", ")"]
|
|
9
|
+
],
|
|
10
|
+
"autoClosingPairs": [
|
|
11
|
+
{ "open": "{", "close": "}", "notIn": ["string", "comment"] },
|
|
12
|
+
{ "open": "[", "close": "]", "notIn": ["string", "comment"] },
|
|
13
|
+
{ "open": "(", "close": ")", "notIn": ["string", "comment"] },
|
|
14
|
+
{ "open": "\"", "close": "\"", "notIn": ["string", "comment"] },
|
|
15
|
+
{ "open": "'", "close": "'", "notIn": ["string", "comment"] }
|
|
16
|
+
],
|
|
17
|
+
"indentationRules": {
|
|
18
|
+
"increaseIndentPattern": "(^.*\\{[^}]*$)",
|
|
19
|
+
"decreaseIndentPattern": "^\\s*\\}"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @enum number
|
|
3
|
+
*/
|
|
4
|
+
export const State = {
|
|
5
|
+
TopLevelContent: 1,
|
|
6
|
+
AfterKeywordDeclaration: 2,
|
|
7
|
+
AfterKeywordStruct: 3,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @enum number
|
|
12
|
+
*/
|
|
13
|
+
export const TokenType = {
|
|
14
|
+
Whitespace: 2,
|
|
15
|
+
Punctuation: 3,
|
|
16
|
+
CurlyOpen: 6,
|
|
17
|
+
CurlyClose: 7,
|
|
18
|
+
PropertyColon: 8,
|
|
19
|
+
Variable: 10,
|
|
20
|
+
None: 57,
|
|
21
|
+
Unknown: 881,
|
|
22
|
+
Numeric: 883,
|
|
23
|
+
NewLine: 884,
|
|
24
|
+
Comment: 885,
|
|
25
|
+
Text: 887,
|
|
26
|
+
FuntionName: 890,
|
|
27
|
+
String: 891,
|
|
28
|
+
KeywordImport: 892,
|
|
29
|
+
Keyword: 123,
|
|
30
|
+
KeywordControl: 124,
|
|
31
|
+
KeywordReturn: 125,
|
|
32
|
+
Type: 126,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const TokenMap = {
|
|
36
|
+
[TokenType.Whitespace]: 'Whitespace',
|
|
37
|
+
[TokenType.Punctuation]: 'Punctuation',
|
|
38
|
+
[TokenType.CurlyOpen]: 'Punctuation',
|
|
39
|
+
[TokenType.CurlyClose]: 'Punctuation',
|
|
40
|
+
[TokenType.PropertyColon]: 'Punctuation',
|
|
41
|
+
[TokenType.Variable]: 'VariableName',
|
|
42
|
+
[TokenType.None]: 'None',
|
|
43
|
+
[TokenType.Unknown]: 'Unknown',
|
|
44
|
+
[TokenType.Numeric]: 'Numeric',
|
|
45
|
+
[TokenType.NewLine]: 'NewLine',
|
|
46
|
+
[TokenType.Comment]: 'Comment',
|
|
47
|
+
[TokenType.Text]: 'Text',
|
|
48
|
+
[TokenType.FuntionName]: 'Function',
|
|
49
|
+
[TokenType.String]: 'String',
|
|
50
|
+
[TokenType.KeywordImport]: 'KeywordImport',
|
|
51
|
+
[TokenType.Keyword]: 'Keyword',
|
|
52
|
+
[TokenType.KeywordControl]: 'KeywordControl',
|
|
53
|
+
[TokenType.KeywordReturn]: 'KeywordReturn',
|
|
54
|
+
[TokenType.Type]: 'Type',
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const RE_WHITESPACE = /^\s+/
|
|
58
|
+
const RE_CURLY_OPEN = /^\{/
|
|
59
|
+
const RE_CURLY_CLOSE = /^\}/
|
|
60
|
+
const RE_PROPERTY_NAME = /^[a-zA-Z\-\w]+/
|
|
61
|
+
const RE_COLON = /^:/
|
|
62
|
+
const RE_SEMICOLON = /^;/
|
|
63
|
+
const RE_COMMA = /^,/
|
|
64
|
+
const RE_ANYTHING = /^.+/s
|
|
65
|
+
const RE_NUMERIC = /^\-?(([0-9]+\.?[0-9]*)|(\.[0-9]+))/
|
|
66
|
+
const RE_BLOCK_COMMENT_START = /^\/\*/
|
|
67
|
+
const RE_BLOCK_COMMENT_END = /^\*\//
|
|
68
|
+
const RE_BLOCK_COMMENT_CONTENT = /^.+?(?=\*\/|$)/s
|
|
69
|
+
const RE_ROUND_OPEN = /^\(/
|
|
70
|
+
const RE_ROUND_CLOSE = /^\)/
|
|
71
|
+
const RE_PSEUDO_SELECTOR_CONTENT = /^[^\)]+/
|
|
72
|
+
const RE_SQUARE_OPEN = /^\[/
|
|
73
|
+
const RE_SQUARE_CLOSE = /^\]/
|
|
74
|
+
const RE_ATTRIBUTE_SELECTOR_CONTENT = /^[^\]]+/
|
|
75
|
+
const RE_QUERY = /^@[a-z\-]+/
|
|
76
|
+
const RE_STAR = /^\*/
|
|
77
|
+
const RE_QUERY_NAME = /^[a-zA-Z\w\-\d\_]+/
|
|
78
|
+
const RE_QUERY_CONTENT = /^[^\)]+/
|
|
79
|
+
const RE_COMBINATOR = /^[\+\>\~]/
|
|
80
|
+
const RE_FUNCTION = /^[a-zA-Z][a-zA-Z\-]+(?=\()/
|
|
81
|
+
const RE_VARIABLE_NAME = /^[a-zA-Z\w\-\_\d]+/
|
|
82
|
+
const RE_PERCENT = /^\%/
|
|
83
|
+
const RE_OPERATOR = /^[\-\/\*\+]/
|
|
84
|
+
const RE_DOUBLE_QUOTE = /^"/
|
|
85
|
+
const RE_STRING_DOUBLE_QUOTE_CONTENT = /^[^"]+/
|
|
86
|
+
const RE_STRING_SINGLE_QUOTE_CONTENT = /^[^']+/
|
|
87
|
+
const RE_SINGLE_QUOTE = /^'/
|
|
88
|
+
const RE_ANYTHING_BUT_CURLY = /^[^\{\}]+/s
|
|
89
|
+
const RE_LINE_COMMENT = /^\/\/.*/s
|
|
90
|
+
const RE_KEYWORD =
|
|
91
|
+
/^(?:var|let|fn|for|struct|vec2|f32|if|elseif|else|mat4x4|vec4|return)\b/
|
|
92
|
+
|
|
93
|
+
const RE_PUNCTUATION = /^[\{\}\[\]\(\)\.\;\<\>\=\+\-\:\*\/\&>\?\@\<\>\,]/
|
|
94
|
+
|
|
95
|
+
export const initialLineState = {
|
|
96
|
+
state: State.TopLevelContent,
|
|
97
|
+
tokens: [],
|
|
98
|
+
stack: [],
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @param {any} lineStateA
|
|
103
|
+
* @param {any} lineStateB
|
|
104
|
+
*/
|
|
105
|
+
export const isEqualLineState = (lineStateA, lineStateB) => {
|
|
106
|
+
return lineStateA.state === lineStateB.state
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const hasArrayReturn = true
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @param {string} line
|
|
113
|
+
* @param {any} lineState
|
|
114
|
+
*/
|
|
115
|
+
export const tokenizeLine = (line, lineState) => {
|
|
116
|
+
let next = null
|
|
117
|
+
let index = 0
|
|
118
|
+
let tokens = []
|
|
119
|
+
let token = TokenType.None
|
|
120
|
+
let state = lineState.state
|
|
121
|
+
const stack = lineState.stack
|
|
122
|
+
while (index < line.length) {
|
|
123
|
+
const part = line.slice(index)
|
|
124
|
+
switch (state) {
|
|
125
|
+
case State.TopLevelContent:
|
|
126
|
+
if ((next = part.match(RE_WHITESPACE))) {
|
|
127
|
+
token = TokenType.Whitespace
|
|
128
|
+
state = State.TopLevelContent
|
|
129
|
+
} else if ((next = part.match(RE_LINE_COMMENT))) {
|
|
130
|
+
token = TokenType.Comment
|
|
131
|
+
state = State.TopLevelContent
|
|
132
|
+
} else if ((next = part.match(RE_KEYWORD))) {
|
|
133
|
+
switch (next[0]) {
|
|
134
|
+
case 'let':
|
|
135
|
+
case 'var':
|
|
136
|
+
token = TokenType.Keyword
|
|
137
|
+
state = State.AfterKeywordDeclaration
|
|
138
|
+
break
|
|
139
|
+
case 'if':
|
|
140
|
+
case 'elseif':
|
|
141
|
+
case 'else':
|
|
142
|
+
token = TokenType.KeywordControl
|
|
143
|
+
state = State.TopLevelContent
|
|
144
|
+
break
|
|
145
|
+
case 'return':
|
|
146
|
+
token = TokenType.KeywordReturn
|
|
147
|
+
state = State.TopLevelContent
|
|
148
|
+
break
|
|
149
|
+
case 'vec4':
|
|
150
|
+
case 'vec2':
|
|
151
|
+
case 'f32':
|
|
152
|
+
case 'i32':
|
|
153
|
+
case 'mat4x4':
|
|
154
|
+
token = TokenType.Type
|
|
155
|
+
state = State.TopLevelContent
|
|
156
|
+
break
|
|
157
|
+
case 'struct':
|
|
158
|
+
token = TokenType.Keyword
|
|
159
|
+
state = State.AfterKeywordStruct
|
|
160
|
+
break
|
|
161
|
+
default:
|
|
162
|
+
token = TokenType.Keyword
|
|
163
|
+
state = State.TopLevelContent
|
|
164
|
+
break
|
|
165
|
+
}
|
|
166
|
+
} else if ((next = part.match(RE_PUNCTUATION))) {
|
|
167
|
+
token = TokenType.Punctuation
|
|
168
|
+
state = State.TopLevelContent
|
|
169
|
+
} else if ((next = part.match(RE_VARIABLE_NAME))) {
|
|
170
|
+
token = TokenType.Variable
|
|
171
|
+
state = State.TopLevelContent
|
|
172
|
+
} else if ((next = part.match(RE_ANYTHING))) {
|
|
173
|
+
token = TokenType.Unknown
|
|
174
|
+
state = State.TopLevelContent
|
|
175
|
+
} else {
|
|
176
|
+
part //?
|
|
177
|
+
throw new Error('no')
|
|
178
|
+
}
|
|
179
|
+
break
|
|
180
|
+
case State.AfterKeywordDeclaration:
|
|
181
|
+
if ((next = part.match(RE_WHITESPACE))) {
|
|
182
|
+
token = TokenType.Whitespace
|
|
183
|
+
state = State.AfterKeywordDeclaration
|
|
184
|
+
} else if ((next = part.match(RE_PUNCTUATION))) {
|
|
185
|
+
token = TokenType.Punctuation
|
|
186
|
+
state = State.AfterKeywordDeclaration
|
|
187
|
+
} else if ((next = part.match(RE_VARIABLE_NAME))) {
|
|
188
|
+
token = TokenType.Variable
|
|
189
|
+
state = State.TopLevelContent
|
|
190
|
+
} else if ((next = part.match(RE_ANYTHING))) {
|
|
191
|
+
token = TokenType.Unknown
|
|
192
|
+
state = State.TopLevelContent
|
|
193
|
+
} else {
|
|
194
|
+
throw new Error('no')
|
|
195
|
+
}
|
|
196
|
+
break
|
|
197
|
+
case State.AfterKeywordStruct:
|
|
198
|
+
if ((next = part.match(RE_WHITESPACE))) {
|
|
199
|
+
token = TokenType.Whitespace
|
|
200
|
+
state = State.AfterKeywordStruct
|
|
201
|
+
} else if ((next = part.match(RE_PUNCTUATION))) {
|
|
202
|
+
token = TokenType.Punctuation
|
|
203
|
+
state = State.TopLevelContent
|
|
204
|
+
} else if ((next = part.match(RE_VARIABLE_NAME))) {
|
|
205
|
+
token = TokenType.Type
|
|
206
|
+
state = State.TopLevelContent
|
|
207
|
+
} else if ((next = part.match(RE_ANYTHING))) {
|
|
208
|
+
token = TokenType.Unknown
|
|
209
|
+
state = State.TopLevelContent
|
|
210
|
+
} else {
|
|
211
|
+
throw new Error('no')
|
|
212
|
+
}
|
|
213
|
+
break
|
|
214
|
+
default:
|
|
215
|
+
throw new Error('no')
|
|
216
|
+
}
|
|
217
|
+
const tokenLength = next[0].length
|
|
218
|
+
index += tokenLength
|
|
219
|
+
tokens.push(token, tokenLength)
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
state,
|
|
223
|
+
tokens,
|
|
224
|
+
stack,
|
|
225
|
+
}
|
|
226
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lvce-editor/shared-process",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.20",
|
|
4
4
|
"description": "Utility package for @lvce-editor/server",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@babel/code-frame": "^7.22.5",
|
|
20
|
-
"@lvce-editor/extension-host": "0.16.
|
|
21
|
-
"@lvce-editor/extension-host-helper-process": "0.16.
|
|
22
|
-
"@lvce-editor/pty-host": "0.16.
|
|
20
|
+
"@lvce-editor/extension-host": "0.16.20",
|
|
21
|
+
"@lvce-editor/extension-host-helper-process": "0.16.20",
|
|
22
|
+
"@lvce-editor/pty-host": "0.16.20",
|
|
23
23
|
"debug": "^4.3.4",
|
|
24
24
|
"execa": "^7.1.1",
|
|
25
25
|
"exit-hook": "^3.2.0",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as ExtensionManifest from '../ExtensionManifest/ExtensionManifest.js'
|
|
2
2
|
import * as ExtensionManifestStatus from '../ExtensionManifestStatus/ExtensionManifestStatus.js'
|
|
3
|
-
import * as FileSystem from '../FileSystem/FileSystem.js'
|
|
4
3
|
import * as Path from '../Path/Path.js'
|
|
5
4
|
import * as Platform from '../Platform/Platform.js'
|
|
5
|
+
import * as RemoveSymlink from '../RemoveSymlink/RemoveSymlink.js'
|
|
6
6
|
import { VError } from '../VError/VError.js'
|
|
7
7
|
|
|
8
8
|
export const unlink = async (path) => {
|
|
@@ -14,7 +14,7 @@ export const unlink = async (path) => {
|
|
|
14
14
|
const linkedExtensionsPath = Platform.getLinkedExtensionsPath()
|
|
15
15
|
// @ts-ignore
|
|
16
16
|
const to = Path.join(linkedExtensionsPath, manifest.id)
|
|
17
|
-
await
|
|
17
|
+
await RemoveSymlink.removeSymlink(to)
|
|
18
18
|
} catch (error) {
|
|
19
19
|
throw new VError(error, `Failed to unlink extension`)
|
|
20
20
|
}
|
|
@@ -169,11 +169,11 @@ export const getSetupName = () => {
|
|
|
169
169
|
return 'Lvce-Setup'
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
export const version = '0.16.
|
|
172
|
+
export const version = '0.16.20'
|
|
173
173
|
|
|
174
|
-
export const commit = '
|
|
174
|
+
export const commit = '2a4c633'
|
|
175
175
|
|
|
176
|
-
export const date = '2023-07-
|
|
176
|
+
export const date = '2023-07-29T10:35:52.000Z'
|
|
177
177
|
|
|
178
178
|
export const getVersion = () => {
|
|
179
179
|
return version
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { rm } from 'node:fs/promises'
|
|
2
|
+
import * as Assert from '../Assert/Assert.js'
|
|
3
|
+
import { VError } from '../VError/VError.js'
|
|
4
|
+
|
|
5
|
+
export const removeSymlink = async (path) => {
|
|
6
|
+
try {
|
|
7
|
+
Assert.string(path)
|
|
8
|
+
await rm(path, { force: true })
|
|
9
|
+
} catch (error) {
|
|
10
|
+
throw new VError(error, `Failed to remove symlink ${path}`)
|
|
11
|
+
}
|
|
12
|
+
}
|