@lvce-editor/shared-process 0.9.2 → 0.10.1

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.
@@ -416,5 +416,14 @@
416
416
  {
417
417
  "id": "Window.zoomReset",
418
418
  "label": "Window: Reset Zoom"
419
+ },
420
+ {
421
+ "id": "Main.openUri",
422
+ "label": "Simple Browser: Open",
423
+ "args": ["simple-browser://"]
424
+ },
425
+ {
426
+ "id": "SimpleBrowser.openDevtools",
427
+ "label": "Simple Browser: Open Dev Devtools"
419
428
  }
420
429
  ]
@@ -747,5 +747,10 @@
747
747
  "key": "Escape",
748
748
  "command": "TitleBarMenuBar.handleKeyEscape",
749
749
  "when": "focus.TitleBarMenuBar"
750
+ },
751
+ {
752
+ "key": "Enter",
753
+ "command": "SimpleBrowser.go",
754
+ "when": "focus.SimpleBrowserInput"
750
755
  }
751
756
  ]
@@ -31,5 +31,12 @@
31
31
  "workbench.sideBar.visible": true,
32
32
  "workbench.saveStateOnVisibilityChange": false,
33
33
 
34
- "workspace.defaultWorkspace": "fetch:///playground"
34
+ "workspace.defaultWorkspace": "fetch:///playground",
35
+
36
+ "keyBindings.fallTroughBrowserView": [
37
+ "ctrl+p",
38
+ "ctrl+shift+p",
39
+ "ctrl+b",
40
+ "ctrl+m"
41
+ ]
35
42
  }
@@ -9,7 +9,7 @@
9
9
  "/playground/languages/index.jl",
10
10
  "/playground/languages/index.js",
11
11
  "/playground/languages/index.kt",
12
- "/playground/languages/index.perl",
12
+ "/playground/languages/index.pl",
13
13
  "/playground/languages/index.py",
14
14
  "/playground/languages/index.ts",
15
15
  "/playground/languages/scrolling.txt",
@@ -14,6 +14,7 @@ export const State = {
14
14
  AfterQuery: 10,
15
15
  InsideRound: 11,
16
16
  AfterQueryWithRules: 12,
17
+ InsideRule: 13,
17
18
  }
18
19
 
19
20
  export const StateMap = {
@@ -60,7 +61,7 @@ export const TokenMap = {
60
61
  [TokenType.CurlyClose]: 'Punctuation',
61
62
  [TokenType.PropertyColon]: 'Punctuation',
62
63
  [TokenType.CssPropertySemicolon]: 'Punctuation',
63
- [TokenType.Variable]: 'Variable',
64
+ [TokenType.Variable]: 'VariableName',
64
65
  [TokenType.None]: 'None',
65
66
  [TokenType.CssPropertyValue]: 'CssPropertyValue',
66
67
  [TokenType.Unknown]: 'Unknown',
@@ -68,12 +69,12 @@ export const TokenMap = {
68
69
  [TokenType.Numeric]: 'Numeric',
69
70
  [TokenType.NewLine]: 'NewLine',
70
71
  [TokenType.Comment]: 'Comment',
71
- [TokenType.Query]: 'Query',
72
+ [TokenType.Query]: 'CssAtRule',
72
73
  [TokenType.Text]: 'Text',
73
74
  [TokenType.CssSelectorId]: 'CssSelectorId',
74
75
  }
75
76
 
76
- const RE_SELECTOR = /^[\.a-zA-Z\d\-\:>\+\~]+/
77
+ const RE_SELECTOR = /^[\.a-zA-Z\d\-\:>\+\~\_%]+/
77
78
  const RE_SELECTOR_ID = /^#\w+/
78
79
  const RE_WHITESPACE = /^ +/
79
80
  const RE_CURLY_OPEN = /^\{/
@@ -151,17 +152,19 @@ export const tokenizeLine = (line, lineState) => {
151
152
  token = TokenType.Punctuation
152
153
  state = State.InsideAttributeSelector
153
154
  } else if ((next = part.match(RE_QUERY))) {
154
- if (
155
- next[0] === '@font-face' ||
156
- next[0] === '@-ms-viewport' ||
157
- next[0] === '@-o-viewport' ||
158
- next[0] === '@viewport'
159
- ) {
160
- token = TokenType.Query
161
- state = State.AfterQueryWithRules
162
- } else {
163
- token = TokenType.Query
164
- state = State.AfterQuery
155
+ switch (next[0]) {
156
+ case '@font-face':
157
+ case '@-ms-viewport':
158
+ case '@-o-viewport':
159
+ case '@viewport':
160
+ token = TokenType.Query
161
+ state = State.AfterQueryWithRules
162
+ break
163
+ case '@keyframes':
164
+ default:
165
+ token = TokenType.Query
166
+ state = State.AfterQuery
167
+ break
165
168
  }
166
169
  } else if ((next = part.match(RE_STAR))) {
167
170
  token = TokenType.CssSelector
@@ -238,7 +241,7 @@ export const tokenizeLine = (line, lineState) => {
238
241
  case State.InsideBlockComment:
239
242
  if ((next = part.match(RE_BLOCK_COMMENT_END))) {
240
243
  token = TokenType.Comment
241
- state = State.TopLevelContent
244
+ state = stack.pop() || State.TopLevelContent
242
245
  } else if ((next = part.match(RE_BLOCK_COMMENT_CONTENT))) {
243
246
  token = TokenType.Comment
244
247
  state = State.InsideBlockComment
@@ -257,6 +260,10 @@ export const tokenizeLine = (line, lineState) => {
257
260
  if (next[0].startsWith('--')) {
258
261
  token = TokenType.Variable
259
262
  }
263
+ } else if ((next = part.match(RE_BLOCK_COMMENT_START))) {
264
+ token = TokenType.Comment
265
+ state = State.InsideBlockComment
266
+ stack.push(State.InsideSelector)
260
267
  } else if ((next = part.match(RE_ANYTHING_UNTIL_CLOSE_BRACE))) {
261
268
  token = TokenType.Unknown
262
269
  state = State.InsideSelector
@@ -85,6 +85,7 @@ const RE_COMMA = /^,/
85
85
  const RE_COLON = /^:/
86
86
  const RE_NUMERIC =
87
87
  /^((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)\b/
88
+ const RE_NUMERIC_OCTAL = /0(?:o|O)?[0-7][0-7_]*(n)?\b/
88
89
  const RE_LINE_COMMENT_START = /^\/\//
89
90
  const RE_LINE_COMMENT_CONTENT = /^[^\n]+/
90
91
  const RE_NEWLINE_WHITESPACE = /^\n\s*/
@@ -94,14 +95,14 @@ const RE_BLOCK_COMMENT_END = /^\*\//
94
95
  const RE_UNKNOWN_VALUE = /^[^\}\{\s,"]+/
95
96
  const RE_IMPORT = /^[a-zA-Z\.]+/
96
97
  const RE_SEMICOLON = /^;/
97
- const RE_VARIABLE_NAME = /^[a-zA-Z_][a-zA-Z\d\_]*/
98
+ const RE_VARIABLE_NAME = /^[a-zA-Z_$][a-zA-Z\d\_]*/
98
99
  const RE_LINE_COMMENT = /^\/\/[^\n]*/
99
100
  const RE_ROUND_OPEN = /^\(/
100
101
  const RE_ROUND_CLOSE = /^\)/
101
102
  const RE_DOT = /^\./
102
103
  const RE_EQUAL_SIGN = /^=/
103
104
  const RE_SINGLE_QUOTE = /^'/
104
- const RE_PUNCTUATION = /^[\(\)=\+\-><\.:;\{\}\[\]!,&\|\^\?\*]/
105
+ const RE_PUNCTUATION = /^[\(\)=\+\-><\.:;\{\}\[\]!,&\|\^\?\*%]/
105
106
  const RE_SLASH = /^\//
106
107
  const RE_ANYTHING_UNTIL_END = /^.+/s
107
108
  const RE_WHITESPACE = /^\s+/
@@ -198,7 +199,7 @@ export const tokenizeLine = (line, lineState) => {
198
199
  token = TokenType.Comment
199
200
  state = State.InsideLineComment
200
201
  } else {
201
- next = part.match(RE_PUNCTUATION)
202
+ next = part.match(RE_SLASH)
202
203
  token = TokenType.Punctuation
203
204
  state = State.TopLevelContent
204
205
  }
@@ -211,6 +212,9 @@ export const tokenizeLine = (line, lineState) => {
211
212
  } else if ((next = part.match(RE_QUOTE_BACKTICK))) {
212
213
  token = TokenType.Punctuation
213
214
  state = State.InsideBacktickString
215
+ } else if ((next = part.match(RE_NUMERIC_OCTAL))) {
216
+ token = TokenType.Numeric
217
+ state = State.TopLevelContent
214
218
  } else {
215
219
  line.slice(0, index) //?
216
220
  part //?
@@ -93,8 +93,10 @@ const RE_ANYTHING = /^.+/s
93
93
  const RE_NUMERIC =
94
94
  /^((0(x|X)[0-9a-fA-F]*)|(([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)\b/
95
95
  const RE_TEXT = /^[^\s\{\}\[\]]+/
96
- const RE_ESCAPED_QUOTE = /^\\"/
97
- const RE_BACK_SLASH = /^\\/
96
+ // const RE_ESCAPED_QUOTE = /^\\"/
97
+ // const RE_BACK_SLASH = /^\\/
98
+ const RE_STRING_ESCAPE = /^\\.?/
99
+ const RE_WORD = /^\w+/
98
100
 
99
101
  export const initialLineState = {
100
102
  state: State.TopLevelContent,
@@ -165,7 +167,6 @@ export const tokenizeLine = (line, lineState) => {
165
167
  }
166
168
  break
167
169
  case State.AfterCurlyOpen:
168
- part
169
170
  if ((next = part.match(RE_WHITESPACE))) {
170
171
  token = TokenType.Whitespace
171
172
  state = State.AfterCurlyOpen
@@ -182,6 +183,12 @@ export const tokenizeLine = (line, lineState) => {
182
183
  } else if ((next = part.match(RE_LINE_COMMENT))) {
183
184
  token = TokenType.Comment
184
185
  state = State.AfterCurlyOpen
186
+ } else if ((next = part.match(RE_WORD))) {
187
+ token = TokenType.Text
188
+ state = State.AfterPropertyName
189
+ } else if ((next = part.match(RE_ANYTHING))) {
190
+ token = TokenType.Text
191
+ state = State.AfterCurlyOpen
185
192
  } else {
186
193
  part //?
187
194
  throw new Error('no')
@@ -195,10 +202,7 @@ export const tokenizeLine = (line, lineState) => {
195
202
  token = TokenType.Punctuation
196
203
  state = stack.pop()
197
204
  state
198
- } else if ((next = part.match(RE_ESCAPED_QUOTE))) {
199
- token = TokenType.JsonPropertyName
200
- state = State.InsidePropertyNameString
201
- } else if ((next = part.match(RE_BACK_SLASH))) {
205
+ } else if ((next = part.match(RE_STRING_ESCAPE))) {
202
206
  token = TokenType.JsonPropertyName
203
207
  state = State.InsidePropertyNameString
204
208
  } else {
@@ -212,10 +216,7 @@ export const tokenizeLine = (line, lineState) => {
212
216
  } else if ((next = part.match(RE_DOUBLE_QUOTE))) {
213
217
  token = TokenType.Punctuation
214
218
  state = stack.pop()
215
- } else if ((next = part.match(RE_ESCAPED_QUOTE))) {
216
- token = TokenType.String
217
- state = State.InsideString
218
- } else if ((next = part.match(RE_BACK_SLASH))) {
219
+ } else if ((next = part.match(RE_STRING_ESCAPE))) {
219
220
  token = TokenType.String
220
221
  state = State.InsideString
221
222
  } else {
@@ -239,7 +240,6 @@ export const tokenizeLine = (line, lineState) => {
239
240
  }
240
241
  break
241
242
  case State.AfterPropertyNameAfterColon:
242
- part
243
243
  if ((next = part.match(RE_WHITESPACE))) {
244
244
  token = TokenType.Whitespace
245
245
  state = State.AfterPropertyNameAfterColon
@@ -5,7 +5,7 @@
5
5
  "languages": [
6
6
  {
7
7
  "id": "perl",
8
- "extensions": [".perl"],
8
+ "extensions": [".pl", ".pm", ".pod", ".t", ".pL", ".psgi"],
9
9
  "tokenize": "src/tokenizePerl.js"
10
10
  }
11
11
  ]
@@ -1,31 +1,146 @@
1
+ /**
2
+ * @enum number
3
+ */
4
+ export const State = {
5
+ TopLevelContent: 1,
6
+ InsideString: 2,
7
+ InsideLineComment: 3,
8
+ }
9
+
10
+ export const StateMap = {
11
+ [State.TopLevelContent]: 'TopLevelContent',
12
+ }
13
+
1
14
  /**
2
15
  * @enum number
3
16
  */
4
17
  export const TokenType = {
5
- Text: 1,
18
+ None: 1,
19
+ Whitespace: 2,
20
+ PunctuationString: 3,
21
+ String: 4,
22
+ Keyword: 5,
23
+ Numeric: 6,
24
+ Punctuation: 7,
25
+ VariableName: 8,
26
+ Comment: 885,
27
+ Text: 9,
6
28
  }
7
29
 
8
30
  export const TokenMap = {
31
+ [TokenType.None]: 'None',
32
+ [TokenType.Whitespace]: 'Whitespace',
33
+ [TokenType.PunctuationString]: 'PunctuationString',
34
+ [TokenType.String]: 'String',
35
+ [TokenType.Keyword]: 'Keyword',
36
+ [TokenType.Numeric]: 'Numeric',
37
+ [TokenType.Punctuation]: 'Punctuation',
38
+ [TokenType.VariableName]: 'VariableName',
39
+ [TokenType.Comment]: 'Comment',
9
40
  [TokenType.Text]: 'Text',
10
41
  }
11
42
 
43
+ const RE_LINE_COMMENT = /^#.*/s
44
+ const RE_SELECTOR = /^[\.a-zA-Z\d\-\:>]+/
45
+ const RE_WHITESPACE = /^ +/
46
+ const RE_CURLY_OPEN = /^\{/
47
+ const RE_CURLY_CLOSE = /^\}/
48
+ const RE_PROPERTY_NAME = /^[a-zA-Z\-]+\b/
49
+ const RE_COLON = /^:/
50
+ const RE_PROPERTY_VALUE = /^[^;\}]+/
51
+ const RE_SEMICOLON = /^;/
52
+ const RE_COMMA = /^,/
53
+ const RE_ANYTHING = /^.+/s
54
+ const RE_ANYTHING_UNTIL_CLOSE_BRACE = /^[^\}]+/
55
+ const RE_QUOTE_DOUBLE = /^"/
56
+ const RE_STRING_DOUBLE_QUOTE_CONTENT = /^[^"]+/
57
+ const RE_KEYWORD =
58
+ /^(?:case|continue|default|die|no|else|elsif|eval|exit|for|foreach|given|goto|if|last|next|redo|require|return|select|switch|unless|until|use|wait|when|while)\b/
59
+
60
+ const RE_VARIABLE_NAME = /^[a-zA-Z\_]+/
61
+ const RE_PUNCTUATION = /^[:,;\{\}\[\]\.=\(\)<>]/
62
+ const RE_NUMERIC = /^\d+/
63
+
12
64
  export const initialLineState = {
13
- state: 0,
65
+ state: State.TopLevelContent,
14
66
  tokens: [],
15
67
  }
16
68
 
69
+ /**
70
+ *
71
+ * @param {any} lineStateA
72
+ * @param {any} lineStateB
73
+ * @returns
74
+ */
75
+ export const isEqualLineState = (lineStateA, lineStateB) => {
76
+ return lineStateA.state === lineStateB.state
77
+ }
78
+
79
+ export const hasArrayReturn = true
80
+
17
81
  /**
18
82
  * @param {string} line
83
+ * @param {any} lineState
19
84
  */
20
- export const tokenizeLine = (line) => {
21
- const tokens = [
22
- {
23
- type: TokenType.Text,
24
- length: line.length,
25
- },
26
- ]
85
+ export const tokenizeLine = (line, lineState) => {
86
+ let next = null
87
+ let index = 0
88
+ let tokens = []
89
+ let token = TokenType.None
90
+ let state = lineState.state
91
+ while (index < line.length) {
92
+ const part = line.slice(index)
93
+ switch (state) {
94
+ case State.TopLevelContent:
95
+ if ((next = part.match(RE_WHITESPACE))) {
96
+ token = TokenType.Whitespace
97
+ state = State.TopLevelContent
98
+ } else if ((next = part.match(RE_KEYWORD))) {
99
+ token = TokenType.Keyword
100
+ state = State.TopLevelContent
101
+ } else if ((next = part.match(RE_PUNCTUATION))) {
102
+ token = TokenType.Punctuation
103
+ state = State.TopLevelContent
104
+ } else if ((next = part.match(RE_VARIABLE_NAME))) {
105
+ token = TokenType.VariableName
106
+ state = State.TopLevelContent
107
+ } else if ((next = part.match(RE_NUMERIC))) {
108
+ token = TokenType.Numeric
109
+ state = State.TopLevelContent
110
+ } else if ((next = part.match(RE_QUOTE_DOUBLE))) {
111
+ token = TokenType.PunctuationString
112
+ state = State.InsideString
113
+ } else if ((next = part.match(RE_LINE_COMMENT))) {
114
+ token = TokenType.Comment
115
+ state = State.TopLevelContent
116
+ } else if ((next = part.match(RE_ANYTHING))) {
117
+ token = TokenType.Text
118
+ state = State.TopLevelContent
119
+ } else {
120
+ part //?
121
+ throw new Error('no')
122
+ }
123
+ break
124
+ case State.InsideString:
125
+ if ((next = part.match(RE_QUOTE_DOUBLE))) {
126
+ token = TokenType.PunctuationString
127
+ state = State.TopLevelContent
128
+ } else if ((next = part.match(RE_STRING_DOUBLE_QUOTE_CONTENT))) {
129
+ token = TokenType.String
130
+ state = State.InsideString
131
+ } else {
132
+ throw new Error('no')
133
+ }
134
+ break
135
+ default:
136
+ throw new Error('no')
137
+ }
138
+ const tokenLength = next[0].length
139
+ index += tokenLength
140
+ tokens.push(token, tokenLength)
141
+ }
27
142
  return {
28
- state: 0,
143
+ state,
29
144
  tokens,
30
145
  }
31
146
  }
@@ -38,6 +38,8 @@ export const TokenType = {
38
38
  String: 188,
39
39
  YamlPropertyName: 123,
40
40
  YamlPropertyValueString: 124,
41
+ Alias: 125,
42
+ Anchor: 126,
41
43
  }
42
44
 
43
45
  export const TokenMap = {
@@ -55,6 +57,8 @@ export const TokenMap = {
55
57
  [TokenType.String]: 'String',
56
58
  [TokenType.YamlPropertyName]: 'YamlPropertyName',
57
59
  [TokenType.YamlPropertyValueString]: 'YamlPropertyValueString',
60
+ [TokenType.Alias]: 'VariableName',
61
+ [TokenType.Anchor]: 'VariableName',
58
62
  }
59
63
 
60
64
  const RE_LINE_COMMENT_START = /^#/
@@ -64,7 +68,7 @@ const RE_CURLY_CLOSE = /^\}/
64
68
  const RE_PROPERTY_NAME = /^[\:@\/\\a-zA-Z\-\_\d\.]+(?=\s*:(\s+|$))/
65
69
  const RE_PROPERTY_NAME_2 =
66
70
  /^[\:@\/\\a-zA-Z\-\_\d\.\s]*[@\/\\a-zA-Z\-\_\d\.](?=\s*:(\s+|$))/
67
- const RE_PROPERTY_VALUE_1 = /^.+(?=\s+#)/s
71
+ const RE_PROPERTY_VALUE_1 = /^[^&].*(?=\s+#)/s
68
72
  const RE_SEMICOLON = /^;/
69
73
  const RE_COMMA = /^,/
70
74
  const RE_ANYTHING = /^.+/s
@@ -93,6 +97,9 @@ const RE_MULTI_LINE_STRING_START = /^(?:\||>\-|>)/
93
97
  const RE_KEY_PRE = /^\s*(\-\s*)?/
94
98
  const RE_SINGLE_QUOTE = /^'/
95
99
  const RE_STRING_SINGLE_QUOTE_CONTENT = /^[^']+/
100
+ const RE_ALIAS = /^\*.+/
101
+ const RE_ANCHOR = /^\&.+/
102
+ const RE_MERGE_KEY = /^<<\:(?=\s)/
96
103
 
97
104
  export const initialLineState = {
98
105
  state: State.TopLevelContent,
@@ -158,6 +165,9 @@ export const tokenizeLine = (line, lineState) => {
158
165
  } else if ((next = part.match(RE_SINGLE_QUOTE))) {
159
166
  token = TokenType.Punctuation
160
167
  state = State.InsidePropertyNameString
168
+ } else if ((next = part.match(RE_MERGE_KEY))) {
169
+ token = TokenType.Punctuation
170
+ state = State.AfterPropertyNameAfterColon
161
171
  } else if ((next = part.match(RE_ANYTHING))) {
162
172
  token = TokenType.Text
163
173
  state = State.TopLevelContent
@@ -204,6 +214,9 @@ export const tokenizeLine = (line, lineState) => {
204
214
  } else if ((next = part.match(RE_PROPERTY_VALUE_1))) {
205
215
  token = TokenType.YamlPropertyValueString
206
216
  state = State.AfterPropertyValue
217
+ } else if ((next = part.match(RE_ANCHOR))) {
218
+ token = TokenType.Anchor
219
+ state = State.AfterPropertyNameAfterColon
207
220
  } else if ((next = part.match(RE_ANYTHING))) {
208
221
  token = TokenType.YamlPropertyValueString
209
222
  state = State.TopLevelContent
@@ -247,6 +260,9 @@ export const tokenizeLine = (line, lineState) => {
247
260
  } else if ((next = part.match(RE_MULTI_LINE_STRING_START))) {
248
261
  token = TokenType.Punctuation
249
262
  state = State.AfterPipe
263
+ } else if ((next = part.match(RE_ALIAS))) {
264
+ token = TokenType.Alias
265
+ state = State.TopLevelContent
250
266
  } else if ((next = part.match(RE_ANYTHING))) {
251
267
  token = TokenType.YamlPropertyValueString
252
268
  state = State.TopLevelContent
@@ -309,6 +325,9 @@ export const tokenizeLine = (line, lineState) => {
309
325
  } else if ((next = part.match(RE_DASH))) {
310
326
  token = TokenType.Punctuation
311
327
  state = State.AfterDash
328
+ } else if ((next = part.match(RE_LINE_COMMENT_START))) {
329
+ token = TokenType.Comment
330
+ state = State.InsideLineComment
312
331
  } else if ((next = part.match(RE_ANYTHING))) {
313
332
  token = TokenType.Text
314
333
  state = State.TopLevelContent
@@ -85,6 +85,10 @@
85
85
  "name": "CssSelector",
86
86
  "foreground": "#BEB558"
87
87
  },
88
+ {
89
+ "name": "CssSelectorId",
90
+ "foreground": "#BE7C81"
91
+ },
88
92
  {
89
93
  "name": "CssPropertyValue",
90
94
  "foreground": "#C0C0C3"
package/index.js CHANGED
@@ -4,3 +4,10 @@ import { fileURLToPath } from 'node:url'
4
4
  const __dirname = dirname(fileURLToPath(import.meta.url))
5
5
 
6
6
  export const sharedProcessPath = join(__dirname, 'src', 'sharedProcessMain.js')
7
+
8
+ export const exportStatic = async () => {
9
+ const fn = await import('./src/parts/ExportStatic/ExportStatic.js')
10
+ const root = process.cwd()
11
+ const pathPrefix = process.env.PATH_PREFIX || ''
12
+ await fn.exportStatic({ root, pathPrefix })
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/shared-process",
3
- "version": "0.9.2",
3
+ "version": "0.10.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -20,12 +20,12 @@
20
20
  "node": ">=16"
21
21
  },
22
22
  "dependencies": {
23
- "@lvce-editor/extension-host": "0.9.2",
24
- "@lvce-editor/pty-host": "0.9.2",
23
+ "@lvce-editor/extension-host": "0.10.1",
24
+ "@lvce-editor/pty-host": "0.10.1",
25
25
  "debug": "^4.3.4",
26
26
  "execa": "^6.1.0",
27
27
  "exit-hook": "^3.1.0",
28
- "got": "^12.5.1",
28
+ "got": "^12.5.2",
29
29
  "is-object": "^1.0.2",
30
30
  "lines-and-columns": "^2.0.3",
31
31
  "open": "^8.4.0",
@@ -42,7 +42,8 @@
42
42
  "optionalDependencies": {
43
43
  "electron-clipboard-ex": "^1.3.3",
44
44
  "keytar": "^7.9.0",
45
- "vscode-ripgrep-with-github-api-error-fix": "^2.5.0"
45
+ "vscode-ripgrep-with-github-api-error-fix": "^2.5.0",
46
+ "extract-zip": "^2.0.1"
46
47
  },
47
48
  "devDependencies": {
48
49
  "@types/debug": "^4.1.7",
@@ -0,0 +1,6 @@
1
+ import * as ChromeExtension from './ChromeExtension.js'
2
+
3
+ export const Commands = {
4
+ 'ChromeExtension.install': ChromeExtension.install,
5
+ 'ChromeExtension.uninstall': ChromeExtension.uninstall,
6
+ }
@@ -0,0 +1,48 @@
1
+ import VError from 'verror'
2
+ import * as Assert from '../Assert/Assert.js'
3
+ import * as Download from '../Download/Download.js'
4
+ import * as ExtractZip from '../ExtractZip/ExtractZip.js'
5
+ import * as FileSystem from '../FileSystem/FileSystem.js'
6
+ import * as Path from '../Path/Path.js'
7
+ import * as Platform from '../Platform/Platform.js'
8
+
9
+ const getExtensionPath = async (tmpDir) => {
10
+ const dirents = await FileSystem.readDirWithFileTypes(tmpDir)
11
+ if (dirents.length === 1 && dirents[0].type === 'directory') {
12
+ return Path.join(tmpDir, dirents[0].name)
13
+ }
14
+ return tmpDir
15
+ }
16
+
17
+ export const install = async (name, url) => {
18
+ try {
19
+ Assert.string(name)
20
+ Assert.string(url)
21
+ const cachedChromeExtensionsPath = Platform.getCachedExtensionsPath()
22
+ const tempFileCompressed = Path.join(
23
+ cachedChromeExtensionsPath,
24
+ `${name}.zip`
25
+ )
26
+ const tmpDir = Path.join(cachedChromeExtensionsPath, name)
27
+ const chromeExtensionsPath = Platform.getChromeExtensionsPath()
28
+ await Download.download(url, tempFileCompressed)
29
+ await ExtractZip.extractZip({ inFile: tempFileCompressed, outDir: tmpDir })
30
+ const cachedExtensionPath = await getExtensionPath(tmpDir)
31
+ const outDir = Path.join(chromeExtensionsPath, name)
32
+ await FileSystem.remove(outDir)
33
+ await FileSystem.rename(cachedExtensionPath, outDir)
34
+ } catch (error) {
35
+ throw new VError(error, `Failed to install chrome extension ${name}`)
36
+ }
37
+ }
38
+
39
+ export const uninstall = async (name) => {
40
+ try {
41
+ Assert.string(name)
42
+ // TODO
43
+ // figure out extension path
44
+ // remove file
45
+ } catch (error) {
46
+ throw new VError(error, `Failed to uninstall chrome extension ${name}`)
47
+ }
48
+ }
@@ -0,0 +1,447 @@
1
+ import fs, { cpSync, readdirSync, readFileSync, writeFileSync } from 'fs'
2
+ import path, { join } from 'path'
3
+
4
+ // TODO
5
+ // - implement this for syntax highlighting projects
6
+ // - implement this for language features web extensions
7
+
8
+ const readExtensionManifest = (path) => {
9
+ const content = readFileSync(path, 'utf8')
10
+ return { ...JSON.parse(content), path }
11
+ }
12
+
13
+ const getThemeName = (dirent) => {
14
+ return dirent.slice('builtin.theme-'.length)
15
+ }
16
+
17
+ /**
18
+ *
19
+ * @param {{root:string, pathPrefix:string }} param0
20
+ */
21
+ export const exportStatic = async ({ root, pathPrefix }) => {
22
+ const dirents = readdirSync(
23
+ join(root, 'node_modules', '@lvce-editor', 'server', 'static')
24
+ )
25
+ const RE_COMMIT_HASH = /^[a-z\d]+$/
26
+ const isCommitHash = (dirent) => {
27
+ return dirent.length === 7 && dirent.match(RE_COMMIT_HASH)
28
+ }
29
+ const commitHash = dirents.find(isCommitHash) || ''
30
+
31
+ const extensionJson = readExtensionManifest(join(root, 'extension.json'))
32
+ const colorThemeName = extensionJson.id.slice('builtin.theme-'.length)
33
+ const name = extensionJson.name || extensionJson.id || ''
34
+ const description = extensionJson.description || ''
35
+ fs.rmSync(join(root, 'dist'), { recursive: true, force: true })
36
+
37
+ fs.mkdirSync(path.join(root, 'dist'))
38
+
39
+ fs.mkdirSync(
40
+ join(
41
+ root,
42
+ 'dist',
43
+ commitHash,
44
+ 'extensions',
45
+ `builtin.theme-${colorThemeName}`
46
+ ),
47
+ {
48
+ recursive: true,
49
+ }
50
+ )
51
+ fs.cpSync(
52
+ join(root, 'node_modules', '@lvce-editor', 'server', 'static'),
53
+ join(root, 'dist'),
54
+ {
55
+ recursive: true,
56
+ }
57
+ )
58
+
59
+ const replaceSync = (path, occurrence, replacement) => {
60
+ const oldContent = readFileSync(path, 'utf8')
61
+ if (!oldContent.includes(occurrence)) {
62
+ throw new Error(`failed to replace occurrence ${occurrence}: Not found`)
63
+ }
64
+ // @ts-ignore
65
+ const newContent = oldContent.replaceAll(occurrence, replacement)
66
+ writeFileSync(path, newContent)
67
+ }
68
+
69
+ replaceSync(
70
+ join(
71
+ root,
72
+ 'dist',
73
+ commitHash,
74
+ 'packages',
75
+ 'renderer-process',
76
+ 'dist',
77
+ 'rendererProcessMain.js'
78
+ ),
79
+ 'platform = getPlatform();',
80
+ 'platform = "web"'
81
+ )
82
+ replaceSync(
83
+ join(
84
+ root,
85
+ 'dist',
86
+ commitHash,
87
+ 'packages',
88
+ 'renderer-process',
89
+ 'dist',
90
+ 'rendererProcessMain.js'
91
+ ),
92
+ `return "/${commitHash}";`,
93
+ `return "${pathPrefix}/${commitHash}";`
94
+ )
95
+ replaceSync(
96
+ join(
97
+ root,
98
+ 'dist',
99
+ commitHash,
100
+ 'packages',
101
+ 'renderer-worker',
102
+ 'dist',
103
+ 'rendererWorkerMain.js'
104
+ ),
105
+ 'platform = getPlatform();',
106
+ 'platform = "web"'
107
+ )
108
+ replaceSync(
109
+ join(
110
+ root,
111
+ 'dist',
112
+ commitHash,
113
+ 'packages',
114
+ 'renderer-worker',
115
+ 'dist',
116
+ 'rendererWorkerMain.js'
117
+ ),
118
+ `platform2 = "remote";`,
119
+ 'platform2 = "web";'
120
+ )
121
+ replaceSync(
122
+ join(
123
+ root,
124
+ 'dist',
125
+ commitHash,
126
+ 'packages',
127
+ 'renderer-worker',
128
+ 'dist',
129
+ 'rendererWorkerMain.js'
130
+ ),
131
+ `return "/${commitHash}";`,
132
+ `return "${pathPrefix}/${commitHash}";`
133
+ )
134
+ replaceSync(
135
+ join(
136
+ root,
137
+ 'dist',
138
+ commitHash,
139
+ 'packages',
140
+ 'renderer-worker',
141
+ 'dist',
142
+ 'rendererWorkerMain.js'
143
+ ),
144
+ `getColorThemeUrlWeb = (colorThemeId) => {
145
+ return \`/extensions/builtin.theme-\${colorThemeId}/color-theme.json\`;
146
+ };`,
147
+ `const getColorThemeUrlWeb = (colorThemeId) => {
148
+ const assetDir = getAssetDir()
149
+ return \`\${assetDir}/themes/\${colorThemeId}.json\`
150
+ }`
151
+ )
152
+ replaceSync(
153
+ join(
154
+ root,
155
+ 'dist',
156
+ commitHash,
157
+ 'packages',
158
+ 'renderer-worker',
159
+ 'dist',
160
+ 'rendererWorkerMain.js'
161
+ ),
162
+ `getIconThemeUrl = (iconThemeId) => {
163
+ return \`/extensions/builtin.\${iconThemeId}/icon-theme.json\`;
164
+ }`,
165
+ `getIconThemeUrl = (iconThemeId) => {
166
+ const assetDir = getAssetDir()
167
+ return \`\${assetDir}/icon-themes/\${iconThemeId}.json\`
168
+ }`
169
+ )
170
+ replaceSync(
171
+ join(
172
+ root,
173
+ 'dist',
174
+ commitHash,
175
+ 'packages',
176
+ 'renderer-worker',
177
+ 'dist',
178
+ 'rendererWorkerMain.js'
179
+ ),
180
+ `return \`\${extensionPath}\${value}\``,
181
+ `return \`${pathPrefix}/${commitHash}/file-icons/\${value.slice(7)}\``
182
+ )
183
+
184
+ // workaround for firefox bug
185
+ replaceSync(
186
+ join(
187
+ root,
188
+ 'dist',
189
+ commitHash,
190
+ 'packages',
191
+ 'renderer-worker',
192
+ 'dist',
193
+ 'rendererWorkerMain.js'
194
+ ),
195
+ `//# sourceMappingURL`,
196
+ `export {}
197
+ //# sourceMappingURL`
198
+ )
199
+ replaceSync(
200
+ join(root, 'dist', commitHash, 'config', 'defaultSettings.json'),
201
+ `"workbench.colorTheme": "slime"`,
202
+ `"workbench.colorTheme": "${colorThemeName}"`
203
+ )
204
+ replaceSync(
205
+ join(root, 'dist', commitHash, 'config', 'defaultSettings.json'),
206
+ `"workbench.saveStateOnVisibilityChange": false`,
207
+ `"workbench.saveStateOnVisibilityChange": true`
208
+ )
209
+
210
+ const extensionDirents = readdirSync(
211
+ join(root, 'node_modules', '@lvce-editor', 'shared-process', 'extensions')
212
+ )
213
+
214
+ const isLanguageBasics = (dirent) => {
215
+ return dirent.startsWith('builtin.language-basics')
216
+ }
217
+
218
+ const isTheme = (dirent) => {
219
+ return dirent.startsWith('builtin.theme-')
220
+ }
221
+
222
+ const isIconTheme = (dirent) => {
223
+ return dirent === 'builtin.vscode-icons'
224
+ }
225
+
226
+ const languageBasicsDirents = extensionDirents.filter(isLanguageBasics)
227
+ const themeDirents = extensionDirents.filter(isTheme)
228
+ const iconThemeDirents = extensionDirents.filter(isIconTheme)
229
+
230
+ const writeJson = (path, json) => {
231
+ const content = JSON.stringify(json, null, 2) + '\n'
232
+ writeFileSync(path, content)
233
+ }
234
+
235
+ const getManifestPath = (dirent) => {
236
+ return join(
237
+ root,
238
+ 'node_modules',
239
+ '@lvce-editor',
240
+ 'shared-process',
241
+ 'extensions',
242
+ dirent,
243
+ 'extension.json'
244
+ )
245
+ }
246
+ const getLanguages = (extension) => {
247
+ const languages = []
248
+ for (const language of extension.languages || []) {
249
+ languages.push({
250
+ ...language,
251
+ tokenize: `${pathPrefix}/${commitHash}/extensions/${extension.id}/${language.tokenize}`,
252
+ })
253
+ }
254
+ return languages
255
+ }
256
+ const languages = languageBasicsDirents
257
+ .map(getManifestPath)
258
+ .map(readExtensionManifest)
259
+ .flatMap(getLanguages)
260
+ writeJson(
261
+ join(root, 'dist', commitHash, 'config', 'languages.json'),
262
+ languages
263
+ )
264
+
265
+ for (const languageBasicsDirent of languageBasicsDirents) {
266
+ cpSync(
267
+ join(
268
+ root,
269
+ 'node_modules',
270
+ '@lvce-editor',
271
+ 'shared-process',
272
+ 'extensions',
273
+ languageBasicsDirent
274
+ ),
275
+ join(root, 'dist', commitHash, 'extensions', languageBasicsDirent),
276
+ {
277
+ recursive: true,
278
+ }
279
+ )
280
+ }
281
+
282
+ for (const themeDirent of themeDirents) {
283
+ const themeId = getThemeName(themeDirent)
284
+ cpSync(
285
+ join(
286
+ root,
287
+ 'node_modules',
288
+ '@lvce-editor',
289
+ 'shared-process',
290
+ 'extensions',
291
+ themeDirent,
292
+ 'color-theme.json'
293
+ ),
294
+ join(root, 'dist', commitHash, 'themes', `${themeId}.json`)
295
+ )
296
+ }
297
+
298
+ const compare = (a, b) => {
299
+ return a.localeCompare(b)
300
+ }
301
+
302
+ const toSorted = (objects, compare) => {
303
+ return [...objects].sort(compare)
304
+ }
305
+
306
+ const mergeThemes = (builtinThemes, extensionThemes) => {
307
+ const seen = []
308
+ const merged = []
309
+ for (const extensionTheme of extensionThemes) {
310
+ seen.push(extensionTheme)
311
+ merged.push(extensionTheme)
312
+ }
313
+ for (const builtinTheme of builtinThemes) {
314
+ if (seen.includes(builtinTheme)) {
315
+ continue
316
+ }
317
+ merged.push(builtinTheme)
318
+ }
319
+ const sorted = toSorted(merged, compare)
320
+ return sorted
321
+ }
322
+
323
+ const themeIds = mergeThemes(themeDirents.map(getThemeName), [colorThemeName])
324
+ writeJson(join(root, 'dist', commitHash, 'config', 'themes.json'), themeIds)
325
+
326
+ for (const iconThemeDirent of iconThemeDirents) {
327
+ const iconThemeId = iconThemeDirent.slice('builtin.'.length)
328
+ cpSync(
329
+ join(
330
+ root,
331
+ 'node_modules',
332
+ '@lvce-editor',
333
+ 'shared-process',
334
+ 'extensions',
335
+ iconThemeDirent,
336
+ 'icon-theme.json'
337
+ ),
338
+ join(root, 'dist', commitHash, 'icon-themes', `${iconThemeId}.json`)
339
+ )
340
+ cpSync(
341
+ join(
342
+ root,
343
+ 'node_modules',
344
+ '@lvce-editor',
345
+ 'shared-process',
346
+ 'extensions',
347
+ iconThemeDirent,
348
+ 'icons'
349
+ ),
350
+ join(root, 'dist', commitHash, 'file-icons'),
351
+ {
352
+ recursive: true,
353
+ }
354
+ )
355
+ }
356
+
357
+ cpSync(
358
+ join(root, 'color-theme.json'),
359
+ join(root, 'dist', commitHash, 'themes', `${colorThemeName}.json`)
360
+ )
361
+ replaceSync(
362
+ join(root, 'dist', 'index.html'),
363
+ `/${commitHash}`,
364
+ `${pathPrefix}/${commitHash}`
365
+ )
366
+ replaceSync(
367
+ join(root, 'dist', 'index.html'),
368
+ `/manifest.json`,
369
+ `${pathPrefix}/manifest.json`
370
+ )
371
+ replaceSync(
372
+ join(root, 'dist', 'index.html'),
373
+ '<title>Code Editor</title>',
374
+ `<title>${name}</title>`
375
+ )
376
+ replaceSync(
377
+ join(root, 'dist', 'manifest.json'),
378
+ `"name": "Code Editor Web - OSS"`,
379
+ `"name": "${name}"`
380
+ )
381
+ replaceSync(
382
+ join(root, 'dist', 'manifest.json'),
383
+ `"short_name": "Web - OSS"`,
384
+ `"short_name": "${name}"`
385
+ )
386
+ replaceSync(
387
+ join(root, 'dist', 'manifest.json'),
388
+ `"description": "Web Code Editor."`,
389
+ `"description": "${description}"`
390
+ )
391
+ replaceSync(
392
+ join(root, 'dist', 'index.html'),
393
+ '<meta name="description" content="Online Code Editor" />',
394
+ `<meta name="description" content="${description}" />`
395
+ )
396
+ replaceSync(
397
+ join(root, 'dist', 'index.html'),
398
+ '</title>',
399
+ `</title>
400
+ <link rel="shortcut icon" type="image/x-icon" href="favicon.ico">`
401
+ )
402
+ replaceSync(
403
+ join(root, 'dist', 'manifest.json'),
404
+ `/${commitHash}`,
405
+ `${pathPrefix}/${commitHash}`
406
+ )
407
+
408
+ fs.copyFileSync(
409
+ join(root, 'README.md'),
410
+ join(
411
+ root,
412
+ 'dist',
413
+ commitHash,
414
+ 'extensions',
415
+ `builtin.theme-${colorThemeName}`,
416
+ 'README.md'
417
+ )
418
+ )
419
+ fs.copyFileSync(
420
+ join(root, 'extension.json'),
421
+ join(
422
+ root,
423
+ 'dist',
424
+ commitHash,
425
+ 'extensions',
426
+ `builtin.theme-${colorThemeName}`,
427
+ 'extension.json'
428
+ )
429
+ )
430
+ fs.copyFileSync(
431
+ join(root, 'color-theme.json'),
432
+ join(
433
+ root,
434
+ 'dist',
435
+ commitHash,
436
+ 'extensions',
437
+ `builtin.theme-${colorThemeName}`,
438
+ 'color-theme.json'
439
+ )
440
+ )
441
+
442
+ replaceSync(
443
+ join(root, 'dist', commitHash, 'css', 'App.css'),
444
+ `/${commitHash}`,
445
+ `${pathPrefix}/${commitHash}`
446
+ )
447
+ }
@@ -0,0 +1,12 @@
1
+ import extract from 'extract-zip'
2
+ import { mkdir } from 'node:fs/promises'
3
+ import VError from 'verror'
4
+
5
+ export const extractZip = async ({ inFile, outDir }) => {
6
+ try {
7
+ await mkdir(outDir, { recursive: true })
8
+ await extract(inFile, { dir: outDir })
9
+ } catch (error) {
10
+ throw new VError(error, `Failed to extract ${inFile}`)
11
+ }
12
+ }
@@ -2,6 +2,8 @@ import * as ModuleId from '../ModuleId/ModuleId.js'
2
2
 
3
3
  export const load = (moduleId) => {
4
4
  switch (moduleId) {
5
+ case ModuleId.ChromeExtension:
6
+ return import('../ChromeExtension/ChromeExtension.ipc.js')
5
7
  case ModuleId.ClipBoard:
6
8
  return import('../ClipBoard/ClipBoard.ipc.js')
7
9
  case ModuleId.Developer:
@@ -14,3 +14,4 @@ export const Terminal = 13
14
14
  export const TextDocument = 14
15
15
  export const WebSocketServer = 15
16
16
  export const Workspace = 16
17
+ export const ChromeExtension = 17
@@ -2,6 +2,9 @@ import * as ModuleId from '../ModuleId/ModuleId.js'
2
2
 
3
3
  export const getModuleId = (commandId) => {
4
4
  switch (commandId) {
5
+ case 'ChromeExtension.install':
6
+ case 'ChromeExtension.uninstall':
7
+ return ModuleId.ChromeExtension
5
8
  case 'ClipBoard.readFiles':
6
9
  case 'ClipBoard.writeFiles':
7
10
  return ModuleId.ClipBoard
@@ -38,10 +38,18 @@ export const getCachedExtensionsPath = () => {
38
38
  return Path.join(cacheDir, 'cached-extensions')
39
39
  }
40
40
 
41
+ export const getCachedChromeExtensionsPath = () => {
42
+ return Path.join(cacheDir, 'cached-electron-browser-view-chrome-extensions')
43
+ }
44
+
41
45
  export const getLinkedExtensionsPath = () => {
42
46
  return Path.join(dataDir, 'linked-extensions')
43
47
  }
44
48
 
49
+ export const getChromeExtensionsPath = () => {
50
+ return Path.join(dataDir, 'electron-browser-view-chrome-extensions')
51
+ }
52
+
45
53
  export const getMarketplaceUrl = () => {
46
54
  return (
47
55
  process.env.LVCE_MARKETPLACE_URL ||